diff --git a/resources/patches-deobfuscated.xml b/resources/patches-deobfuscated.xml
index d9b5a245..e23e1fe2 100644
--- a/resources/patches-deobfuscated.xml
+++ b/resources/patches-deobfuscated.xml
@@ -28,6 +28,7 @@
addPlayer,networkTick
+ networkTick
networkTick
@@ -38,10 +39,12 @@
func_82740_a,scheduleBlockUpdateFromLoad,getPendingBlockUpdates,addBlockEvent,tickUpdates
tickBlocksAndAmbiance
+ tickBlocksAndAmbiance
+ updateEntities
releaseEntitySkin
@@ -138,6 +141,7 @@
+ updatePlayerInstances
addPlayer,removePlayer,updateMountedMovingPlayer,updatePlayerInstances
diff --git a/src/common/javassist/is/faulty/Timings.java b/src/common/javassist/is/faulty/Timings.java
new file mode 100644
index 00000000..07eb5cde
--- /dev/null
+++ b/src/common/javassist/is/faulty/Timings.java
@@ -0,0 +1,111 @@
+package javassist.is.faulty;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.Functions;
+import com.google.common.collect.Ordering;
+
+import me.nallar.tickthreading.util.MappingUtil;
+import me.nallar.tickthreading.util.TableFormatter;
+import org.cliffc.high_scale_lib.NonBlockingHashMap;
+
+public enum Timings {
+ ;
+ public static boolean enabled = false;
+
+ public static void record(String name, long time) {
+ if (time < 0) {
+ time = 0;
+ }
+ getTime(name).addAndGet(time);
+ getInvocationCount(name).incrementAndGet();
+ }
+
+ public static void clear() {
+ invocationCount.clear();
+ time.clear();
+ }
+
+ public static TableFormatter writeData(TableFormatter tf) {
+ Map time = new HashMap();
+ for (Map.Entry entry : Timings.time.entrySet()) {
+ time.put(entry.getKey(), entry.getValue().get());
+ }
+ final List sortedKeysByTime = Ordering.natural().reverse().onResultOf(Functions.forMap(time)).immutableSortedCopy(time.keySet());
+ tf
+ .heading("Class")
+ .heading("Time");
+ for (int i = 0; i < 5 && i < sortedKeysByTime.size(); i++) {
+ tf
+ .row(niceName(sortedKeysByTime.get(i)))
+ .row(time.get(sortedKeysByTime.get(i)) / 1000000d);
+ }
+ tf.finishTable();
+ tf.sb.append('\n');
+ Map timePerTick = new HashMap();
+ for (Map.Entry entry : Timings.time.entrySet()) {
+ timePerTick.put(entry.getKey(), entry.getValue().get() / invocationCount.get(entry.getKey()).get());
+ }
+ final List sortedKeysByTimePerTick = Ordering.natural().reverse().onResultOf(Functions.forMap(timePerTick)).immutableSortedCopy(timePerTick.keySet());
+ tf
+ .heading("Class")
+ .heading("Time/tick");
+ for (int i = 0; i < 5 && i < sortedKeysByTimePerTick.size(); i++) {
+ tf
+ .row(niceName(sortedKeysByTimePerTick.get(i)))
+ .row(timePerTick.get(sortedKeysByTimePerTick.get(i)) / 1000000d);
+ }
+ tf.finishTable();
+ return tf;
+ }
+
+ private static String niceName(String clazz) {
+ int slash = clazz.lastIndexOf('/');
+ String suffix = clazz.substring(slash);
+ String name = MappingUtil.debobfuscate(clazz.substring(0, slash));
+ if (name.contains(".")) {
+ String cName = name.substring(name.lastIndexOf('.') + 1);
+ String pName = name.substring(0, name.lastIndexOf('.'));
+ if (pName.contains(".")) {
+ pName = pName.substring(pName.lastIndexOf('.') + 1);
+ }
+ return pName + '.' + cName + suffix;
+ }
+ return name + suffix;
+ }
+
+ private static final Map invocationCount = new NonBlockingHashMap();
+ private static final Map time = new NonBlockingHashMap();
+
+ private static AtomicInteger getInvocationCount(String clazz) {
+ AtomicInteger i = invocationCount.get(clazz);
+ if (i == null) {
+ synchronized (Timings.class) {
+ i = invocationCount.get(clazz);
+ if (i == null) {
+ i = new AtomicInteger();
+ invocationCount.put(clazz, i);
+ }
+ }
+ }
+ return i;
+ }
+
+ private static AtomicLong getTime(String clazz) {
+ AtomicLong t = time.get(clazz);
+ if (t == null) {
+ synchronized (Timings.class) {
+ t = time.get(clazz);
+ if (t == null) {
+ t = new AtomicLong();
+ time.put(clazz, t);
+ }
+ }
+ }
+ return t;
+ }
+}
diff --git a/src/common/me/nallar/tickthreading/mappings/MethodDescription.java b/src/common/me/nallar/tickthreading/mappings/MethodDescription.java
index 080124d5..e28ffdc2 100644
--- a/src/common/me/nallar/tickthreading/mappings/MethodDescription.java
+++ b/src/common/me/nallar/tickthreading/mappings/MethodDescription.java
@@ -11,7 +11,7 @@
import me.nallar.tickthreading.util.CollectionsUtil;
public class MethodDescription {
- private final String clazz;
+ public final String clazz;
private final String returnType;
private final String parameters;
private final String name;
diff --git a/src/common/me/nallar/tickthreading/minecraft/commands/ProfileCommand.java b/src/common/me/nallar/tickthreading/minecraft/commands/ProfileCommand.java
index d1a73a6e..7740d7fc 100644
--- a/src/common/me/nallar/tickthreading/minecraft/commands/ProfileCommand.java
+++ b/src/common/me/nallar/tickthreading/minecraft/commands/ProfileCommand.java
@@ -2,6 +2,7 @@
import java.util.List;
+import javassist.is.faulty.Timings;
import me.nallar.tickthreading.Log;
import me.nallar.tickthreading.minecraft.TickManager;
import me.nallar.tickthreading.minecraft.TickThreading;
@@ -28,11 +29,15 @@ public boolean canCommandSenderUseCommand(ICommandSender commandSender) {
public void processCommand(final ICommandSender commandSender, List arguments) {
World world = DimensionManager.getWorld(0);
long time_ = 10;
+ boolean entity_ = false;
try {
if (!arguments.isEmpty()) {
- time_ = Integer.valueOf(arguments.get(0));
+ entity_ = "e".equals(arguments.get(0));
}
if (arguments.size() > 1) {
+ time_ = Integer.valueOf(arguments.get(0));
+ }
+ if (arguments.size() > 2) {
world = DimensionManager.getWorld(Integer.valueOf(arguments.get(1)));
} else if (commandSender instanceof Entity) {
world = ((Entity) commandSender).worldObj;
@@ -41,12 +46,17 @@ public void processCommand(final ICommandSender commandSender, List argu
world = null;
}
if (world == null) {
- sendChat(commandSender, "Usage: /profile [time=10] [dimensionid=current dimension]");
+ sendChat(commandSender, "Usage: /profile [type=a/e] [time=10] [dimensionid=current dimension]");
return;
}
final TickManager manager = TickThreading.instance.getManager(world);
final long time = time_;
- manager.profilingEnabled = true;
+ final boolean entity = entity_;
+ if (entity) {
+ manager.profilingEnabled = true;
+ } else {
+ Timings.enabled = true;
+ }
Runnable profilingRunnable = new Runnable() {
@Override
public void run() {
@@ -54,12 +64,20 @@ public void run() {
Thread.sleep(1000 * time);
} catch (InterruptedException ignored) {
}
- manager.profilingEnabled = false;
+ if (entity) {
+ manager.profilingEnabled = false;
+ } else {
+ Timings.enabled = false;
+ }
try {
Thread.sleep(100 * time);
} catch (InterruptedException ignored) {
}
- sendChat(commandSender, String.valueOf(manager.entityTickProfiler.writeData(new TableFormatter(commandSender))));
+ if (entity) {
+ sendChat(commandSender, String.valueOf(manager.entityTickProfiler.writeData(new TableFormatter(commandSender))));
+ } else {
+ sendChat(commandSender, String.valueOf(Timings.writeData(new TableFormatter(commandSender))));
+ }
manager.entityTickProfiler.clear();
}
};
diff --git a/src/common/me/nallar/tickthreading/minecraft/patched/PatchEntityPlayerMP.java b/src/common/me/nallar/tickthreading/minecraft/patched/PatchEntityPlayerMP.java
index f0247d2e..1c4f6e8b 100644
--- a/src/common/me/nallar/tickthreading/minecraft/patched/PatchEntityPlayerMP.java
+++ b/src/common/me/nallar/tickthreading/minecraft/patched/PatchEntityPlayerMP.java
@@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Iterator;
+import javassist.is.faulty.Timings;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemInWorldManager;
import net.minecraft.network.packet.Packet29DestroyEntity;
@@ -42,6 +43,10 @@ public void onUpdate() {
synchronized (loadedChunks) {
if (!this.loadedChunks.isEmpty()) {
+ long st = 0;
+ if (Timings.enabled) {
+ st = System.nanoTime();
+ }
ArrayList var6 = new ArrayList();
Iterator var7 = this.loadedChunks.iterator();
ArrayList var8 = new ArrayList();
@@ -52,13 +57,11 @@ public void onUpdate() {
int z = var9.chunkZPos;
var7.remove();
- if (this.worldObj.blockExists(var9.chunkXPos << 4, 0, var9.chunkZPos << 4)) {
- var6.add(this.worldObj.getChunkFromChunkCoords(var9.chunkXPos, var9.chunkZPos));
- //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it.
- //ToDo: Find a way to efficiently clean abandoned chunks.
- //var8.addAll(((WorldServer) this.worldObj).getAllTileEntityInBox(var9.chunkXPos * 16, 0, var9.chunkZPos * 16, var9.chunkXPos * 16 + 16, 256, var9.chunkZPos * 16 + 16));
- var8.addAll(((WorldServer) this.worldObj).getAllTileEntityInBox(var9.chunkXPos * 16, 0, var9.chunkZPos * 16, var9.chunkXPos * 16 + 15, 256, var9.chunkZPos * 16 + 15));
- }
+ var6.add(this.worldObj.getChunkFromChunkCoords(x, z));
+ //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it.
+ //ToDo: Find a way to efficiently clean abandoned chunks.
+ //var8.addAll(((WorldServer) this.worldObj).getAllTileEntityInBox(var9.chunkXPos * 16, 0, var9.chunkZPos * 16, var9.chunkXPos * 16 + 16, 256, var9.chunkZPos * 16 + 16));
+ var8.addAll(((WorldServer) this.worldObj).getAllTileEntityInBox(x * 16, 0, z * 16, x * 16 + 15, 256, z * 16 + 15));
}
if (!var6.isEmpty()) {
@@ -78,6 +81,7 @@ public void onUpdate() {
MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(var10.getChunkCoordIntPair(), this));
}
}
+ Timings.record("net.minecraft.entity.player.EntityPlayerMP/chunks", System.nanoTime() - st);
}
}
}
diff --git a/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerInstance.java b/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerInstance.java
index e84a4cbf..b13e5f28 100644
--- a/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerInstance.java
+++ b/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerInstance.java
@@ -28,7 +28,7 @@ public static void staticConstruct() {
@Override
public void sendThisChunkToPlayer(EntityPlayerMP par1EntityPlayerMP) {
- if (this.playersInChunk.contains(par1EntityPlayerMP)) {
+ if (this.playersInChunk.remove(par1EntityPlayerMP)) {
Packet51MapChunk packet51MapChunk = new Packet51MapChunk();
packet51MapChunk.includeInitialize = true;
packet51MapChunk.xCh = chunkLocation.chunkXPos;
@@ -37,7 +37,6 @@ public void sendThisChunkToPlayer(EntityPlayerMP par1EntityPlayerMP) {
packet51MapChunk.yChMin = 0;
packet51MapChunk.setData(unloadSequence);
par1EntityPlayerMP.playerNetServerHandler.sendPacketToPlayer(packet51MapChunk);
- this.playersInChunk.remove(par1EntityPlayerMP);
par1EntityPlayerMP.loadedChunks.remove(this.chunkLocation);
MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.UnWatch(chunkLocation, par1EntityPlayerMP));
@@ -46,13 +45,11 @@ public void sendThisChunkToPlayer(EntityPlayerMP par1EntityPlayerMP) {
long var2 = (long) this.chunkLocation.chunkXPos + 2147483647L | (long) this.chunkLocation.chunkZPos + 2147483647L << 32;
this.myManager.getChunkWatchers().remove(var2);
- if (this.numberOfTilesToUpdate > 0) {
- this.myManager.playerUpdateLock.lock();
- try {
- this.myManager.getChunkWatcherWithPlayers().remove(this);
- } finally {
- this.myManager.playerUpdateLock.unlock();
- }
+ this.myManager.playerUpdateLock.lock();
+ try {
+ this.myManager.getChunkWatcherWithPlayers().remove(this);
+ } finally {
+ this.myManager.playerUpdateLock.unlock();
}
this.myManager.getWorldServer().theChunkProviderServer.unloadChunksIfNotNearSpawn(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos);
diff --git a/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerManagerForge.java b/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerManagerForge.java
index 78792d51..925bfce7 100644
--- a/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerManagerForge.java
+++ b/src/common/me/nallar/tickthreading/minecraft/patched/PatchPlayerManagerForge.java
@@ -47,7 +47,7 @@ public void updatePlayerInstances() {
}
}
} catch (Exception e) {
- Log.severe("Failed to unload some chunks", e);
+ Log.severe("Failed to send some chunks", e);
} finally {
playersUpdateLock.unlock();
}
diff --git a/src/common/me/nallar/tickthreading/patcher/PatchManager.java b/src/common/me/nallar/tickthreading/patcher/PatchManager.java
index bc293ab1..483dc1cd 100644
--- a/src/common/me/nallar/tickthreading/patcher/PatchManager.java
+++ b/src/common/me/nallar/tickthreading/patcher/PatchManager.java
@@ -116,6 +116,7 @@ public void obfuscate(Mappings mappings) {
for (Element patchElement : DomUtil.elementList(patchElements)) {
List methodDescriptionList = MethodDescription.fromListString(deobfuscatedClass.name, patchElement.getTextContent());
if (!patchElement.getTextContent().isEmpty()) {
+ patchElement.setAttribute("deobf", methodDescriptionList.get(0).getShortName());
patchElement.setTextContent(MethodDescription.toListString(mappings.map(methodDescriptionList)));
}
String field = patchElement.getAttribute("field"), prefix = "";
diff --git a/src/common/me/nallar/tickthreading/patcher/Patches.java b/src/common/me/nallar/tickthreading/patcher/Patches.java
index fea2c938..b67f872d 100644
--- a/src/common/me/nallar/tickthreading/patcher/Patches.java
+++ b/src/common/me/nallar/tickthreading/patcher/Patches.java
@@ -44,6 +44,22 @@ public void markDirty(CtClass ctClass) {
// A NOOP patch to make sure META-INF is removed
}
+ @Patch
+ public void profile(CtMethod ctMethod, Map attributes) throws CannotCompileException {
+ CtClass ctClass = ctMethod.getDeclaringClass();
+ CtMethod replacement = CtNewMethod.copy(ctMethod, ctClass, null);
+ int i = 0;
+ try {
+ for (; true; i++) {
+ ctClass.getDeclaredMethod(ctMethod.getName() + "_t" + i);
+ }
+ } catch (NotFoundException ignored) {
+ }
+ ctMethod.setName(ctMethod.getName() + "_t" + i);
+ replacement.setBody("{ long st = 0; if (javassist.is.faulty.Timings.enabled) { st = System.nanoTime(); } " + ctMethod.getName() + "($$); if (javassist.is.faulty.Timings.enabled) { javassist.is.faulty.Timings.record(\"" + attributes.get("deobf") + "\", System.nanoTime() - st); } }");
+ ctClass.addMethod(replacement);
+ }
+
@Patch (
name = "volatile"
)