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" )