perf: a tank looks each neighbour tile up once per tick
Build / build (push) Skipped
Build / publish (push) Skipped
Build / build (pull_request) Successful in 5m55s
Build / publish (pull_request) Skipped

updateEntity asked the world for the tile above up to three times and the tile
below up to two times per tick; the blocks around cannot change between those
calls (a taint explosion earlier in the tick is handled before the first
lookup), so each is read once. Results identical, pinned by a test that runs
the old lookup sequence next to the new one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
layue13andClaude Sonnet 5.5 committed 2026-11-08 01:59:24 +08:00
1 parent 579c7064e2
commit 3b4bc76cc6
2 files changed
+160 -4

No files matched your search

@@ -46,14 +46,17 @@ public class TileTankBasic extends TileThaumcraft implements ITubable {
}
}
boolean var4 = this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord) instanceof TileTankBasic && this.vis + this.taint < this.capacity;
// each neighbour tile is looked up once; nothing between here and the uses changes the blocks around
net.minecraft.tileentity.TileEntity aboveTile = this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord);
boolean var4 = aboveTile instanceof TileTankBasic && this.vis + this.taint < this.capacity;
if(var4 && this.taint + this.vis > this.capacity - 5) {
MagicType var5 = Math.max(this.taint, this.vis) == this.vis?MagicType.VIS:MagicType.TAINT;
this.ejectVis(var5, ((ITubable)this.worldObj.getTileEntity(this.xCoord, this.yCoord + 1, this.zCoord)).acceptVis(var5, var5 == MagicType.VIS?this.vis:this.taint, ForgeDirection.DOWN), ForgeDirection.UP);
this.ejectVis(var5, ((ITubable)aboveTile).acceptVis(var5, var5 == MagicType.VIS?this.vis:this.taint, ForgeDirection.DOWN), ForgeDirection.UP);
}
if(this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord) instanceof TileTankBasic) {
TileTankBasic var6 = (TileTankBasic)this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord);
net.minecraft.tileentity.TileEntity belowTile = this.worldObj.getTileEntity(this.xCoord, this.yCoord - 1, this.zCoord);
if(belowTile instanceof TileTankBasic) {
TileTankBasic var6 = (TileTankBasic)belowTile;
if(var6.vis + var6.taint < var6.capacity - 5) {
for(int i = 5; i > 0; --i) {
if(this.vis >= i && var6.vis + var6.taint + i <= this.capacity - 5) {
@@ -0,0 +1,153 @@
package com.pengu.thaumcraft.additions.tileentity;
import static org.junit.Assert.assertEquals;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraft.world.WorldProviderSurface;
import net.minecraft.world.WorldServer;
import org.junit.Test;
/** TDLM/thaumicadditions#20: a tank looks each neighbour tile up once per tick and moves vis/taint exactly as before. */
public class TileTankBasicLookupTest {
@SuppressWarnings("unchecked")
private static <T> T allocate(Class<T> type) throws Exception {
Constructor<?> ctor = sun.reflect.ReflectionFactory.getReflectionFactory()
.newConstructorForSerialization(type, Object.class.getDeclaredConstructor());
return (T) ctor.newInstance();
}
public static class CountingWorld extends WorldServer {
Map<Long, TileEntity> tiles;
int tileLookups;
int blockLookups;
CountingWorld() {
super(null, null, "tanks", 0, null, null);
}
static long key(int x, int y, int z) {
return ((long) x << 40) ^ ((long) y << 20) ^ z;
}
@Override
public TileEntity getTileEntity(int x, int y, int z) {
this.tileLookups++;
return this.tiles.get(key(x, y, z));
}
@Override
public Block getBlock(int x, int y, int z) {
this.blockLookups++;
return null; // equals Blocks.air, which is unset without a Minecraft bootstrap
}
@Override
public int getBlockMetadata(int x, int y, int z) {
this.blockLookups++;
return 0;
}
}
private static CountingWorld world() throws Exception {
CountingWorld world = allocate(CountingWorld.class);
world.rand = new Random(1);
Field provider = World.class.getDeclaredField("provider");
provider.setAccessible(true);
provider.set(world, new WorldProviderSurface());
world.isRemote = true; // no taint explosion branch
world.tiles = new HashMap<Long, TileEntity>();
return world;
}
private static TileTankBasic tank(CountingWorld world, int y, int vis, int taint) throws Exception {
TileTankBasic tank = allocate(TileTankBasic.class);
tank.setWorldObj(world);
tank.xCoord = 3;
tank.yCoord = y;
tank.zCoord = 5;
tank.capacity = 1005;
tank.vis = vis;
tank.taint = taint;
world.tiles.put(CountingWorld.key(3, y, 5), tank);
return tank;
}
/** The lookups of the old method, in order, on the same fields. */
private static void referenceUpdate(TileTankBasic t) {
World w = t.getWorldObj();
w.getBlock(t.xCoord, t.yCoord + 1, t.zCoord);
w.getBlock(t.xCoord, t.yCoord - 1, t.zCoord);
w.getBlockMetadata(t.xCoord, t.yCoord, t.zCoord);
boolean var4 = w.getTileEntity(t.xCoord, t.yCoord + 1, t.zCoord) instanceof TileTankBasic && t.vis + t.taint < t.capacity;
if (var4 && t.taint + t.vis > t.capacity - 5) {
TileTankBasic above = (TileTankBasic) w.getTileEntity(t.xCoord, t.yCoord + 1, t.zCoord);
int type = Math.max(t.taint, t.vis) == t.vis ? 0 : 1;
int q = above.acceptVis(type == 0 ? com.pengu.api.thaumicadditions.tubesyst.ITubable.MagicType.VIS : com.pengu.api.thaumicadditions.tubesyst.ITubable.MagicType.TAINT,
type == 0 ? t.vis : t.taint, net.minecraftforge.common.util.ForgeDirection.DOWN);
t.ejectVis(type == 0 ? com.pengu.api.thaumicadditions.tubesyst.ITubable.MagicType.VIS : com.pengu.api.thaumicadditions.tubesyst.ITubable.MagicType.TAINT, q,
net.minecraftforge.common.util.ForgeDirection.UP);
}
if (w.getTileEntity(t.xCoord, t.yCoord - 1, t.zCoord) instanceof TileTankBasic) {
TileTankBasic var6 = (TileTankBasic) w.getTileEntity(t.xCoord, t.yCoord - 1, t.zCoord);
if (var6.vis + var6.taint < var6.capacity - 5) {
for (int i = 5; i > 0; --i) {
if (t.vis >= i && var6.vis + var6.taint + i <= t.capacity - 5) {
var6.vis += i;
t.vis -= i;
}
if (t.taint >= i && var6.vis + var6.taint + i <= t.capacity - 5) {
var6.taint += i;
t.taint -= i;
}
}
}
}
}
private static int[] state(TileTankBasic... tanks) {
int[] out = new int[tanks.length * 2];
for (int i = 0; i < tanks.length; i++) {
out[2 * i] = tanks[i].vis;
out[2 * i + 1] = tanks[i].taint;
}
return out;
}
@Test
public void aStackOfTanksEndsInTheSameStateWithFewerLookups() throws Exception {
Random rnd = new Random(9);
for (int round = 0; round < 40; round++) {
int[] seedValues = new int[6];
for (int i = 0; i < 6; i++) {
seedValues[i] = rnd.nextInt(1000);
}
CountingWorld oldWorld = world();
CountingWorld newWorld = world();
TileTankBasic[] oldTanks = new TileTankBasic[3];
TileTankBasic[] newTanks = new TileTankBasic[3];
for (int i = 0; i < 3; i++) {
oldTanks[i] = tank(oldWorld, 64 + i, seedValues[2 * i], seedValues[2 * i + 1] % 500);
newTanks[i] = tank(newWorld, 64 + i, seedValues[2 * i], seedValues[2 * i + 1] % 500);
}
for (int tick = 0; tick < 30; tick++) {
for (int i = 0; i < 3; i++) {
referenceUpdate(oldTanks[i]);
newTanks[i].updateEntity();
}
org.junit.Assert.assertArrayEquals("round " + round + " tick " + tick, state(oldTanks), state(newTanks));
}
org.junit.Assert.assertTrue("the old method looks up at least as often", oldWorld.tileLookups >= newWorld.tileLookups);
assertEquals("new: two tile lookups per tank per tick", 2 * 3 * 30, newWorld.tileLookups);
assertEquals(oldWorld.blockLookups, newWorld.blockLookups);
}
}
}