diff --git a/CraftBukkit-Patches/0146-Alternative-Hopper-Ticking.patch b/CraftBukkit-Patches/0146-Alternative-Hopper-Ticking.patch new file mode 100644 index 0000000000..d4bf4580a7 --- /dev/null +++ b/CraftBukkit-Patches/0146-Alternative-Hopper-Ticking.patch @@ -0,0 +1,479 @@ +From e8b30c53ce2cd6e2be450d6d03f8977c79bcc231 Mon Sep 17 00:00:00 2001 +From: "Evan A. Haskell" +Date: Sat, 19 Apr 2014 16:58:26 -0400 +Subject: [PATCH] Alternative Hopper Ticking + + +diff --git a/src/main/java/net/minecraft/server/BlockHopper.java b/src/main/java/net/minecraft/server/BlockHopper.java +index 9e64702..36682df 100644 +--- a/src/main/java/net/minecraft/server/BlockHopper.java ++++ b/src/main/java/net/minecraft/server/BlockHopper.java +@@ -87,6 +87,14 @@ public class BlockHopper extends BlockContainer { + + if (flag != flag1) { + world.setData(i, j, k, i1 | (flag ? 0 : 8), 4); ++ // Spigot start - When this hopper becomes unpowered, make it active. ++ if (world.spigotConfig.altHopperTicking) { ++ TileEntityHopper hopper = e((IBlockAccess) world, i, j, k); ++ if (flag && hopper != null) { ++ hopper.makeTick(); ++ } ++ } ++ // Spigot end + } + } + +@@ -163,4 +171,16 @@ public class BlockHopper extends BlockContainer { + public static TileEntityHopper e(IBlockAccess iblockaccess, int i, int j, int k) { + return (TileEntityHopper) iblockaccess.getTileEntity(i, j, k); + } ++ ++ // Spigot start - Use random block updates to make hoppers active. ++ @Override ++ public void a(World world, int i, int j, int k, Random random) { ++ if (world.spigotConfig.altHopperTicking) { ++ TileEntityHopper hopper = e((IBlockAccess) world, i, j, k); ++ if (hopper != null) { ++ hopper.makeTick(); ++ } ++ } ++ } ++ // Spigot end + } +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 364df1d..5771fbd 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -722,6 +722,11 @@ public class Chunk { + + tileentity.t(); + this.tileEntities.put(chunkposition, tileentity); ++ // Spigot start - We have a world, now we can be sure hoppers are born ticking. ++ if (this.world.spigotConfig.altHopperTicking) { ++ this.world.triggerHoppersList.add(tileentity); ++ } ++ // Spigot end + // CraftBukkit start + } else { + System.out.println("Attempted to place a tile entity (" + tileentity + ") at " + tileentity.x + "," + tileentity.y + "," + tileentity.z +diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java +index 5eb893f..f9e16ac 100644 +--- a/src/main/java/net/minecraft/server/EntityItem.java ++++ b/src/main/java/net/minecraft/server/EntityItem.java +@@ -102,6 +102,26 @@ public class EntityItem extends Entity { + if (this.onGround) { + this.motY *= -0.5D; + } ++ // Spigot start - Make the hopper below this item active. ++ if (this.world.spigotConfig.altHopperTicking) { ++ int xi = MathHelper.floor(this.boundingBox.a); ++ int yi = MathHelper.floor(this.boundingBox.b) - 1; ++ int zi = MathHelper.floor(this.boundingBox.c); ++ int xf = MathHelper.floor(this.boundingBox.d); ++ int yf = MathHelper.floor(this.boundingBox.e) - 1; ++ int zf = MathHelper.floor(this.boundingBox.f); ++ for (int a = xi; a <= xf; a++) { ++ for (int c = zi; c <= zf; c++) { ++ for (int b = yi; b <= yf; b++) { ++ TileEntity tileEntity = this.world.getTileEntity(a, b, c); ++ if (tileEntity instanceof TileEntityHopper) { ++ ((TileEntityHopper) tileEntity).makeTick(); ++ } ++ } ++ } ++ } ++ } ++ // Spigot end + + // ++this.age; // CraftBukkit - Moved up + if (!this.world.isStatic && this.age >= world.spigotConfig.itemDespawnRate) { // Spigot +diff --git a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java +index 166206c..1305526 100644 +--- a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java ++++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java +@@ -342,6 +342,26 @@ public abstract class EntityMinecartAbstract extends Entity { + + this.passenger = null; + } ++ // Spigot start - Make hoppers adjacent to this container minecart active. ++ if (this.world.spigotConfig.altHopperTicking && this instanceof EntityMinecartContainer) { ++ int xi = MathHelper.floor(this.boundingBox.a) - 1; ++ int yi = MathHelper.floor(this.boundingBox.b) - 1; ++ int zi = MathHelper.floor(this.boundingBox.c) - 1; ++ int xf = MathHelper.floor(this.boundingBox.d) + 1; ++ int yf = MathHelper.floor(this.boundingBox.e) + 1; ++ int zf = MathHelper.floor(this.boundingBox.f) + 1; ++ for (int a = xi; a <= xf; a++) { ++ for (int b = yi; b <= yf; b++) { ++ for (int c = zi; c <= zf; c++) { ++ TileEntity tileEntity = this.world.getTileEntity(a, b, c); ++ if (tileEntity instanceof TileEntityHopper) { ++ ((TileEntityHopper) tileEntity).makeTick(); ++ } ++ } ++ } ++ } ++ } ++ // Spigot end + } + } + +diff --git a/src/main/java/net/minecraft/server/EntityOcelot.java b/src/main/java/net/minecraft/server/EntityOcelot.java +index 603e53d..b5fe441 100644 +--- a/src/main/java/net/minecraft/server/EntityOcelot.java ++++ b/src/main/java/net/minecraft/server/EntityOcelot.java +@@ -27,6 +27,29 @@ public class EntityOcelot extends EntityTameableAnimal { + this.datawatcher.a(18, Byte.valueOf((byte) 0)); + } + ++ // Spigot start - When this ocelot begins standing, any chests below this ocelot must be ++ // updated as if its contents have changed. ++ @Override ++ public void h() { ++ if (this.world.spigotConfig.altHopperTicking && this.isSitting()) { ++ int xi = MathHelper.floor(this.boundingBox.a); ++ int yi = MathHelper.floor(this.boundingBox.b) - 1; ++ int zi = MathHelper.floor(this.boundingBox.c); ++ int xf = MathHelper.floor(this.boundingBox.d); ++ int yf = MathHelper.floor(this.boundingBox.e) - 1; ++ int zf = MathHelper.floor(this.boundingBox.f); ++ for (int a = xi; a <= xf; a++) { ++ for (int c = zi; c <= zf; c++) { ++ for (int b = yi; b <= yf; b++) { ++ this.world.updateChestAndHoppers(a, b, c); ++ } ++ } ++ } ++ } ++ super.h(); ++ } ++ // Spigot end ++ + public void bo() { + if (this.getControllerMove().a()) { + double d0 = this.getControllerMove().b(); +diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java +index 19d008c..4a0ca46 100644 +--- a/src/main/java/net/minecraft/server/TileEntity.java ++++ b/src/main/java/net/minecraft/server/TileEntity.java +@@ -24,6 +24,36 @@ public class TileEntity { + public int g = -1; + public Block h; + ++ // Spigot start ++ // Used for alternative ticking. If the hopper at x0, y0, z0 is pointed at this tile entity, make it active. ++ private void scheduleTick(int x0, int y0, int z0) { ++ TileEntity tileEntity = this.world.getTileEntity(x0, y0, z0); ++ if (tileEntity instanceof TileEntityHopper && tileEntity.world != null) { ++ int i = BlockHopper.b(tileEntity.p()); ++ if (tileEntity.x + Facing.b[i] == this.x && tileEntity.y + Facing.c[i] == this.y && tileEntity.z + Facing.d[i] == this.z) { ++ ((TileEntityHopper) tileEntity).makeTick(); ++ } ++ } ++ } ++ ++ // Contents have changed, so hoppers need ticking. Check all 6 faces. ++ public void scheduleTicks() { ++ if (this.world != null && this.world.spigotConfig.altHopperTicking) { ++ // check the top ++ this.scheduleTick(this.x, this.y + 1, this.z); ++ // check the sides ++ for (int i = 2; i < 6; i++) { ++ this.scheduleTick(this.x + Facing.b[i], this.y, this.z + Facing.d[i]); ++ } ++ // check the bottom ++ TileEntity tileEntity = this.world.getTileEntity(this.x, this.y - 1, this.z); ++ if (tileEntity instanceof TileEntityHopper && tileEntity.world != null) { ++ ((TileEntityHopper) tileEntity).makeTick(); ++ } ++ } ++ } ++ // Spigot end ++ + public TileEntity() {} + + private static void a(Class oclass, String s) { +@@ -105,6 +135,9 @@ public class TileEntity { + if (this.q() != Blocks.AIR) { + this.world.f(this.x, this.y, this.z, this.q()); + } ++ // Spigot start - The contents have changed, so make the hoppers pointed at this inventory active. ++ this.scheduleTicks(); ++ // Spigot end + } + } + +diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java +index b620394..02c07cd 100644 +--- a/src/main/java/net/minecraft/server/TileEntityHopper.java ++++ b/src/main/java/net/minecraft/server/TileEntityHopper.java +@@ -17,6 +17,42 @@ public class TileEntityHopper extends TileEntity implements IHopper { + private String i; + private int j = -1; + ++ // Spigot start ++ private long nextTick = -1; // Next tick this hopper will be ticked ++ private long lastTick = -1; // Last tick this hopper was polled ++ ++ // To be used only with the alternative ticking. If this hopper is not cooling down, assaign a visible tick for next time. ++ public void makeTick() { ++ if (!this.j()) { ++ this.c(0); ++ } ++ } ++ ++ // Contents changed, so make this hopper active. ++ public void scheduleHopperTick() { ++ if (this.world != null && this.world.spigotConfig.altHopperTicking) { ++ this.makeTick(); ++ } ++ } ++ ++ // We can't use config or c() if we don't have a world, so this is called when this hopper is assaigned a world. ++ // Is also called when alt hopper ticking is turned on from the reload command ++ public void convertToScheduling() { ++ this.c(this.j); ++ } ++ ++ // Called when alt hopper ticking is turned off from the reload command ++ public void convertToPolling() { ++ long cooldownDiff; ++ if (this.lastTick == this.world.getTime()) { ++ cooldownDiff = this.nextTick - this.world.getTime(); ++ } else { ++ cooldownDiff = this.nextTick - this.world.getTime() + 1; ++ } ++ this.c((int) Math.max(0, Math.min(cooldownDiff, Integer.MAX_VALUE))); ++ } ++ // Spigot end ++ + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; +@@ -80,7 +116,19 @@ public class TileEntityHopper extends TileEntity implements IHopper { + } + + nbttagcompound.set("Items", nbttaglist); +- nbttagcompound.setInt("TransferCooldown", this.j); ++ // Spigot start - Need to write the correct cooldown to disk. We convert from long to int on saving. ++ if (this.world != null && this.world.spigotConfig.altHopperTicking) { ++ long cooldownDiff; ++ if (this.lastTick == this.world.getTime()) { ++ cooldownDiff = this.nextTick - this.world.getTime(); ++ } else { ++ cooldownDiff = this.nextTick - this.world.getTime() + 1; ++ } ++ nbttagcompound.setInt("TransferCooldown", (int) Math.max(0, Math.min(cooldownDiff, Integer.MAX_VALUE))); ++ } else { ++ nbttagcompound.setInt("TransferCooldown", this.j); ++ } ++ // Spigot end + if (this.k_()) { + nbttagcompound.setString("CustomName", this.i); + } +@@ -88,6 +136,9 @@ public class TileEntityHopper extends TileEntity implements IHopper { + + public void update() { + super.update(); ++ // Spigot start - The contents have changed, so make this hopper active. ++ this.scheduleHopperTick(); ++ // Spigot end + } + + public int getSize() { +@@ -167,11 +218,20 @@ public class TileEntityHopper extends TileEntity implements IHopper { + + public void h() { + if (this.world != null && !this.world.isStatic) { +- --this.j; +- if (!this.j()) { +- this.c(0); +- this.i(); ++ // Spigot start ++ if (this.world.spigotConfig.altHopperTicking) { ++ this.lastTick = this.world.getTime(); ++ if (this.nextTick == this.world.getTime()) { ++ this.i(); ++ } ++ } else { ++ --this.j; ++ if (!this.j()) { ++ this.c(0); ++ this.i(); ++ } + } ++ // Spigot end + } + } + +@@ -196,7 +256,7 @@ public class TileEntityHopper extends TileEntity implements IHopper { + } + + // Spigot start +- if ( !this.j() ) ++ if ( !world.spigotConfig.altHopperTicking && !this.j() ) + { + this.c( world.spigotConfig.hopperCheck ); + } +@@ -584,10 +644,34 @@ public class TileEntityHopper extends TileEntity implements IHopper { + } + + public void c(int i) { +- this.j = i; ++ // Spigot start - i is the delay for which this hopper will be ticked next. ++ // i of 1 or below implies a tick next tick. ++ if (this.world != null && this.world.spigotConfig.altHopperTicking) { ++ if (i <= 0) { ++ i = 1; ++ } ++ if (this.lastTick == this.world.getTime()) { ++ this.nextTick = this.world.getTime() + i; ++ } else { ++ this.nextTick = this.world.getTime() + i - 1; ++ } ++ } else { ++ this.j = i; ++ } ++ // Spigot end + } + + public boolean j() { +- return this.j > 0; ++ // Spigot start - Return whether this hopper is cooling down. ++ if (this.world != null && this.world.spigotConfig.altHopperTicking) { ++ if (this.lastTick == this.world.getTime()) { ++ return this.nextTick > this.world.getTime(); ++ } else { ++ return this.nextTick >= this.world.getTime(); ++ } ++ } else { ++ return this.j > 0; ++ } ++ // Spigot end + } + } +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index a879ae3..3559bfc 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -113,6 +113,7 @@ public abstract class World implements IBlockAccess { + private final byte chunkTickRadius; + public static boolean haveWeSilencedAPhysicsCrash; + public static String blockLocation; ++ public List triggerHoppersList = new ArrayList(); // Spigot + + public static long chunkToKey(int x, int z) + { +@@ -130,6 +131,40 @@ public abstract class World implements IBlockAccess { + { + return (int) ( ( ( k >> 32 ) & 0xFFFF0000L ) | ( ( k >> 16 ) & 0x0000FFFF ) ); + } ++ ++ // Spigot Start ++ private void initializeHoppers() { ++ if (this.spigotConfig.altHopperTicking) { ++ for (TileEntity o : this.triggerHoppersList) { ++ o.scheduleTicks(); ++ if (o instanceof TileEntityHopper) { ++ ((TileEntityHopper) o).convertToScheduling(); ++ ((TileEntityHopper) o).scheduleHopperTick(); ++ } ++ } ++ } ++ triggerHoppersList.clear(); ++ } ++ ++ // To be used only with alt hopper ticking ++ public void updateChestAndHoppers(int a, int b, int c) { ++ Block block = this.getType(a, b, c); ++ if (block instanceof BlockChest) { ++ TileEntity tile = this.getTileEntity(a, b, c); ++ if (tile instanceof TileEntityChest) { ++ tile.scheduleTicks(); ++ } ++ for (int i = 2; i < 6; i++) { ++ if (this.getType(a + Facing.b[i], b, c + Facing.d[i]) == block) { ++ tile = this.getTileEntity(a + Facing.b[i], b, c + Facing.d[i]); ++ if (tile instanceof TileEntityChest) { ++ tile.scheduleTicks(); ++ } ++ break; ++ } ++ } ++ } ++ } + // Spigot end + + public BiomeBase getBiome(int i, int j) { +@@ -404,6 +439,12 @@ public abstract class World implements IBlockAccess { + this.notifyAndUpdatePhysics(i, j, k, chunk, block1, block, i1); + // CraftBukkit end + } ++ // Spigot start - If this block is changing to that which a chest beneath it ++ // becomes able to be opened, then said chest must be updated ++ if (this.spigotConfig.altHopperTicking && block1 != null && block1.r() && !block.r()) { ++ this.updateChestAndHoppers(i, j - 1, k); ++ } ++ // Spigot end + + return flag; + } +@@ -1455,6 +1496,7 @@ public abstract class World implements IBlockAccess { + } + // Spigot End + this.M = true; ++ this.initializeHoppers(); // Spigot - hopper ticking + Iterator iterator = this.tileEntityList.iterator(); + + while (iterator.hasNext()) { +diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java +index 3e6b1e6..a81b7c9 100644 +--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java ++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java +@@ -166,11 +166,35 @@ public class SpigotWorldConfig + log( "Entity Tracking Range: Pl " + playerTrackingRange + " / An " + animalTrackingRange + " / Mo " + monsterTrackingRange + " / Mi " + miscTrackingRange + " / Other " + otherTrackingRange ); + } + ++ public boolean altHopperTicking; + public int hopperTransfer; + public int hopperCheck; + public int hopperAmount; + private void hoppers() + { ++ // Alternate ticking method. Uses inventory changes, redstone updates etc. ++ // to update hoppers. Hopper-check is disabled when this is true. ++ boolean prev = altHopperTicking; ++ altHopperTicking = getBoolean( "hopper-alt-ticking", false ); ++ // Necessary for the reload command ++ if (prev != altHopperTicking) { ++ net.minecraft.server.World world = (net.minecraft.server.World) Bukkit.getWorld(this.worldName); ++ if (world != null) { ++ if (altHopperTicking) { ++ for (Object o : world.tileEntityList) { ++ if (o instanceof net.minecraft.server.TileEntityHopper) { ++ ((net.minecraft.server.TileEntityHopper) o).convertToScheduling(); ++ } ++ } ++ } else { ++ for (Object o : world.tileEntityList) { ++ if (o instanceof net.minecraft.server.TileEntityHopper) { ++ ((net.minecraft.server.TileEntityHopper) o).convertToPolling(); ++ } ++ } ++ } ++ } ++ } + // Set the tick delay between hopper item movements + hopperTransfer = getInt( "ticks-per.hopper-transfer", 8 ); + // Set the tick delay between checking for items after the associated +@@ -178,6 +202,7 @@ public class SpigotWorldConfig + // hopper sorting machines from becoming out of sync. + hopperCheck = getInt( "ticks-per.hopper-check", hopperTransfer ); + hopperAmount = getInt( "hopper-amount", 1 ); ++ log( "Alternative Hopper Ticking: " + altHopperTicking ); + log( "Hopper Transfer: " + hopperTransfer + " Hopper Check: " + hopperCheck + " Hopper Amount: " + hopperAmount ); + } + +-- +1.9.1 +