From dd8aa3f1e67f6339ed38fec6ec2a1a86db18d696 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 b85b72f..b33ed64 100644 --- a/src/main/java/net/minecraft/server/BlockHopper.java +++ b/src/main/java/net/minecraft/server/BlockHopper.java @@ -87,6 +87,17 @@ 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. + // Called when this block's power level changes. flag1 is the current + // isNotPowered from metadata. flag is the recalculated isNotPowered. + if (world.spigotConfig.altHopperTicking) { + // e returns the TileEntityHopper associated with this BlockHopper. + TileEntityHopper hopper = e((IBlockAccess) world, i, j, k); + if (flag && hopper != null) { + hopper.makeTick(); + } + } + // Spigot end } } @@ -163,4 +174,17 @@ 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) { + // e returns the TileEntityHopper associated with this BlockHopper. + 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 cbcc9e9..0423ee9 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -750,6 +750,11 @@ public class Chunk { tileentity.t(); this.tileEntities.put(chunkposition, tileentity); + // Spigot start - The tile entity has a world, now hoppers can be 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 98a4ac7..a61e91a 100644 --- a/src/main/java/net/minecraft/server/EntityItem.java +++ b/src/main/java/net/minecraft/server/EntityItem.java @@ -102,6 +102,27 @@ public class EntityItem extends Entity { if (this.onGround) { this.motY *= -0.5D; } + // Spigot start - Make the hopper(s) below this item active. + // Called each tick on each item entity. + 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 f1ccd3a..2a1e69d 100644 --- a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java +++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java @@ -342,6 +342,27 @@ public abstract class EntityMinecartAbstract extends Entity { this.passenger = null; } + // Spigot start - Make hoppers around this container minecart active. + // Called each tick on each minecart. + 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 22aac7b..8abbcdf 100644 --- a/src/main/java/net/minecraft/server/EntityOcelot.java +++ b/src/main/java/net/minecraft/server/EntityOcelot.java @@ -28,6 +28,31 @@ public class EntityOcelot extends EntityTameableAnimal { this.datawatcher.a(18, Byte.valueOf((byte) 0)); } + // Spigot start - When this ocelot begins standing, chests below this ocelot must be + // updated as if its contents have changed. We update chests if this ocelot is sitting + // knowing that it may be dead, gone, or standing after this method returns. + // Called each tick on each ocelot. + @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 bp() { 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 ccea0b3..3c5ec6f 100644 --- a/src/main/java/net/minecraft/server/TileEntity.java +++ b/src/main/java/net/minecraft/server/TileEntity.java @@ -24,6 +24,40 @@ public class TileEntity { public int g = -1; public Block h; + // Spigot start + // Helper method for scheduleTicks. If the hopper at x0, y0, z0 is pointed + // at this tile entity, then 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) { + // i is the metadeta assoiated with the direction the hopper faces. + int i = BlockHopper.b(tileEntity.p()); + // Facing class provides arrays for direction offset. + 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(); + } + } + } + + // Called from update when the contents have changed, so hoppers need updates. + // 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 +139,10 @@ public class TileEntity { if (this.q() != Blocks.AIR) { this.world.updateAdjacentComparators(this.x, this.y, this.z, this.q()); } + // Spigot start - Called when the contents have changed, so hoppers around this + // tile need updating. + 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 ace3617..65ee96c 100644 --- a/src/main/java/net/minecraft/server/TileEntityHopper.java +++ b/src/main/java/net/minecraft/server/TileEntityHopper.java @@ -17,6 +17,43 @@ 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. + + // 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(); + } + } + + // Called after this hopper is assaigned a world or when altHopperTicking is turned + // on from reload. + public void convertToScheduling() { + // j is the cooldown in ticks + 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 +117,20 @@ 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 { + // j is the cooldown in ticks. + nbttagcompound.setInt("TransferCooldown", this.j); + } + // Spigot end if (this.k_()) { nbttagcompound.setString("CustomName", this.i); } @@ -88,6 +138,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 +220,21 @@ 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()) { + // Method that does the pushing and pulling. + this.i(); + } + } else { + --this.j; + if (!this.j()) { + this.c(0); + this.i(); + } } + // Spigot end } } @@ -196,7 +259,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 +647,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 c4fb3d8..6100297 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, When altHopperTicking, tile entities being added go through here. public static long chunkToKey(int x, int z) { @@ -130,6 +131,42 @@ public abstract class World implements IBlockAccess { { return (int) ( ( ( k >> 32 ) & 0xFFFF0000L ) | ( ( k >> 16 ) & 0x0000FFFF ) ); } + + // Spigot Start - Hoppers need to be born ticking. + 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(); + } + + // Helper method for altHopperTicking. Updates chests at the specified location, + // accounting for double chests. Updating the chest will update adjacent hoppers. + 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++) { + // Facing class provides arrays for direction offset. + 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) { @@ -407,6 +444,14 @@ 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 the chest must be updated. + // block1 is the old block. block is the new block. r returns true if the block type + // prevents access to a chest. + if (this.spigotConfig.altHopperTicking && block1 != null && block1.r() && !block.r()) { + this.updateChestAndHoppers(i, j - 1, k); + } + // Spigot end return flag; } @@ -1433,8 +1478,9 @@ public abstract class World implements IBlockAccess { this.tileEntityList.removeAll(this.b); this.b.clear(); } - // CraftBukkit end + // Spigot End + this.initializeHoppers(); // Spigot - Initializes hoppers which have been added recently. 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 2073732..fd79b25 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