Skip to content

Commit

Permalink
Fixed chunkloading issues, added more profiling.
Browse files Browse the repository at this point in the history
Signed-off-by: Ross Allan <rallanpcl@gmail.com>
  • Loading branch information
LunNova committed Jan 30, 2013
1 parent 4ca919e commit 44ead1a
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 23 deletions.
4 changes: 4 additions & 0 deletions resources/patches-deobfuscated.xml
Expand Up @@ -28,6 +28,7 @@
</class>
<class id="net.minecraft.network.NetworkListenThread">
<synchronize>addPlayer,networkTick</synchronize>
<profile>networkTick</profile>
<replaceMethodCall code="{ $_ = null; }" method="log" index="1">networkTick</replaceMethodCall>
</class>
<class id="net.minecraft.network.TcpWriterThread">
Expand All @@ -38,10 +39,12 @@
<synchronize>func_82740_a,scheduleBlockUpdateFromLoad,getPendingBlockUpdates,addBlockEvent,tickUpdates</synchronize>
<addAll fromClass="me.nallar.tickthreading.minecraft.patched.PatchWorldServer"/>
<insertSuper>tickBlocksAndAmbiance</insertSuper>
<profile>tickBlocksAndAmbiance</profile>
</class>
<class id="net.minecraft.world.World">
<!-- Same list was used by multiple threads -->
<addAll fromClass="me.nallar.tickthreading.minecraft.patched.PatchWorld"/>
<profile>updateEntities</profile>
<public>releaseEntitySkin</public>
<newInitializer field="activeChunkSet" code="java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap());"/>
</class>
Expand Down Expand Up @@ -138,6 +141,7 @@
</class>
<class id="net.minecraft.server.management.PlayerManager">
<addAll fromClass="me.nallar.tickthreading.minecraft.patched.PatchPlayerManager"/>
<profile>updatePlayerInstances</profile>
</class>
<class id="net.minecraft.server.management.PlayerManager" env="mcpc">
<synchronize>addPlayer,removePlayer,updateMountedMovingPlayer,updatePlayerInstances</synchronize>
Expand Down
111 changes: 111 additions & 0 deletions 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<String, Long> time = new HashMap<String, Long>();
for (Map.Entry<String, AtomicLong> entry : Timings.time.entrySet()) {
time.put(entry.getKey(), entry.getValue().get());
}
final List<String> 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<String, Long> timePerTick = new HashMap<String, Long>();
for (Map.Entry<String, AtomicLong> entry : Timings.time.entrySet()) {
timePerTick.put(entry.getKey(), entry.getValue().get() / invocationCount.get(entry.getKey()).get());
}
final List<String> 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<String, AtomicInteger> invocationCount = new NonBlockingHashMap<String, AtomicInteger>();
private static final Map<String, AtomicLong> time = new NonBlockingHashMap<String, AtomicLong>();

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;
}
}
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;
Expand All @@ -28,11 +29,15 @@ public boolean canCommandSenderUseCommand(ICommandSender commandSender) {
public void processCommand(final ICommandSender commandSender, List<String> 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;
Expand All @@ -41,25 +46,38 @@ public void processCommand(final ICommandSender commandSender, List<String> 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() {
try {
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();
}
};
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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()) {
Expand All @@ -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);
}
}
}
Expand Down
Expand Up @@ -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;
Expand All @@ -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));
Expand All @@ -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);
Expand Down
Expand Up @@ -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();
}
Expand Down
Expand Up @@ -116,6 +116,7 @@ public void obfuscate(Mappings mappings) {
for (Element patchElement : DomUtil.elementList(patchElements)) {
List<MethodDescription> 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 = "";
Expand Down
16 changes: 16 additions & 0 deletions src/common/me/nallar/tickthreading/patcher/Patches.java
Expand Up @@ -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<String, String> 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"
)
Expand Down

0 comments on commit 44ead1a

Please sign in to comment.