9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-22 08:19:26 +00:00
Files
SakuraMC/sakura-server/minecraft-patches/features/0025-Optimise-hopper-ticking.patch
2025-04-07 01:37:23 +01:00

321 lines
15 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Mon, 12 Aug 2024 15:35:57 +0100
Subject: [PATCH] Optimise hopper ticking
diff --git a/net/minecraft/world/CompoundContainer.java b/net/minecraft/world/CompoundContainer.java
index 0e9beb7f74e527a95bff064631e6d591f5775ce5..f89a8a3a1f3c87432c8816c3273c7b25b324055b 100644
--- a/net/minecraft/world/CompoundContainer.java
+++ b/net/minecraft/world/CompoundContainer.java
@@ -53,6 +53,15 @@ public class CompoundContainer implements Container {
return this.container1.getLocation(); // TODO: right?
}
// CraftBukkit end
+ // Sakura start - optimise hopper ticking
+ @Override
+ public final boolean addListener(net.minecraft.world.level.block.entity.BlockEntity.BlockEntityChangeListener listener) {
+ boolean result = false;
+ result |= this.container1.addListener(listener);
+ result |= this.container2.addListener(listener);
+ return result;
+ }
+ // Sakura end - optimise hopper ticking
public CompoundContainer(Container container1, Container container2) {
this.container1 = container1;
diff --git a/net/minecraft/world/Container.java b/net/minecraft/world/Container.java
index b382665cc125b8b5c0938e5e55984e4bf91d37ff..0c4358053076416fc2cd3f5c9b7f6d2f630cab10 100644
--- a/net/minecraft/world/Container.java
+++ b/net/minecraft/world/Container.java
@@ -14,6 +14,12 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public interface Container extends Clearable, Iterable<ItemStack> {
float DEFAULT_DISTANCE_BUFFER = 4.0F;
+ // Sakura start - optimise hopper ticking
+ default boolean addListener(BlockEntity.BlockEntityChangeListener container) {
+ return false;
+ }
+ // Sakura end - optimise hopper ticking
+
int getContainerSize();
boolean isEmpty();
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 769887ee6d798d0b11de72e9ffe80c9d358daf17..701a48ba3b0f4515a791684d63b3454dbed317f9 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1483,7 +1483,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
// Spigot end
if (tickingBlockEntity.isRemoved()) {
toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll
- } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) {
+ } else if (runsNormally && tickingBlockEntity.isBlockEntityActive() && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { // Sakura - optimise hopper ticking
tickingBlockEntity.tick();
// Paper start - rewrite chunk system
if ((++tickedEntities & 7) == 0) {
diff --git a/net/minecraft/world/level/block/HopperBlock.java b/net/minecraft/world/level/block/HopperBlock.java
index 46a27f60ba407dacdac190b5e292ab3f1db5a078..ec05bb86803d878867b46e437cc73a39f155b9fc 100644
--- a/net/minecraft/world/level/block/HopperBlock.java
+++ b/net/minecraft/world/level/block/HopperBlock.java
@@ -121,6 +121,12 @@ public class HopperBlock extends BaseEntityBlock {
private void checkPoweredState(Level level, BlockPos pos, BlockState state) {
boolean flag = !level.hasNeighborSignal(pos);
if (flag != state.getValue(ENABLED)) {
+ // Sakura start - optimise hopper ticking
+ final BlockEntity blockEntity = level.getBlockEntity(pos);
+ if (blockEntity instanceof HopperBlockEntity hbe && level.sakuraConfig().technical.optimiseIdleHopperTicking) {
+ hbe.setBlockEntityTicking(flag);
+ }
+ // Sakura end - optimise hopper ticking
level.setBlock(pos, state.setValue(ENABLED, flag), 2);
}
}
diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java
index a1075c26d55cc01219acd94d0138f81aa9d34c48..6aa0624eb99223dc13478a24f74a49654019fba5 100644
--- a/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -48,6 +48,60 @@ public abstract class BlockEntity {
private BlockState blockState;
private DataComponentMap components = DataComponentMap.EMPTY;
+ // Sakura start - optimise hopper ticking
+ private final Set<BlockEntityChangeListener> listeners = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0);
+ private final java.util.List<BlockEntity> listeningBlocks = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(0);
+ private boolean blockEntityTicking = true;
+ private int tickCount = 0;
+
+ public final int getIdleTickCount() {
+ return this.tickCount;
+ }
+
+ public final boolean isBlockEntityActive() {
+ // For some reason the update method is not always called.
+ // Activate the block entity every minute in case there's any issues.
+ if (!this.blockEntityTicking && this.tickCount > 1200) {
+ this.setBlockEntityTicking(true);
+ }
+ this.tickCount++;
+ return this.blockEntityTicking;
+ }
+
+ public final void setBlockEntityTicking(boolean blockEntityTicking) {
+ this.tickCount = 0;
+ this.blockEntityTicking = blockEntityTicking;
+ }
+
+ public final boolean addListener(BlockEntityChangeListener listener) {
+ if (this.listeners.add(listener)) {
+ ((BlockEntity) listener).listeningBlocks.add(this);
+ }
+ return true;
+ }
+
+ public final void updateListeners(boolean onRemove) {
+ for (BlockEntityChangeListener listener : this.listeners) {
+ if (onRemove) {
+ listener.neighborRemoved();
+ } else {
+ listener.neighborChange();
+ }
+ }
+ if (onRemove) {
+ this.listeningBlocks.forEach(blockEntity -> blockEntity.listeners.clear());
+ this.listeningBlocks.clear();
+ this.listeners.clear();
+ }
+ }
+
+ public interface BlockEntityChangeListener {
+ void neighborChange();
+
+ void neighborRemoved();
+ }
+ // Sakura end - optimise hopper ticking
+
public BlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState) {
this.type = type;
this.worldPosition = pos.immutable();
@@ -204,11 +258,22 @@ public abstract class BlockEntity {
public void setChanged() {
if (this.level != null) {
if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers
- setChanged(this.level, this.worldPosition, this.blockState);
+ setChanged(this.level, this.worldPosition, this.blockState, this); // Sakura - optimise hopper ticking
}
}
protected static void setChanged(Level level, BlockPos pos, BlockState state) {
+ // Sakura start - optimise hopper ticking
+ net.minecraft.world.level.chunk.LevelChunk chunk = level.getChunkIfLoaded(pos);
+ BlockEntity blockEntity = chunk != null ? chunk.getBlockEntity(pos) : null;
+ setChanged(level, pos, state, blockEntity);
+ }
+
+ protected static void setChanged(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity) {
+ if (blockEntity != null) {
+ blockEntity.updateListeners(false);
+ }
+ // Sakura end - optimise hopper ticking
level.blockEntityChanged(pos);
if (!state.isAir()) {
level.updateNeighbourForOutputSignal(pos, state.getBlock());
@@ -238,6 +303,7 @@ public abstract class BlockEntity {
public void setRemoved() {
this.remove = true;
+ this.updateListeners(true); // Sakura - optimise hopper ticking
}
public void clearRemoved() {
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 15d4f60942c0cc612c1468b4c0fda886867a67cb..98856a692b3ae5ac46cb67678642709b4f931ee7 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -28,7 +28,7 @@ import net.minecraft.world.level.block.HopperBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
-public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper {
+public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, BlockEntity.BlockEntityChangeListener { // Sakura - optimise hopper ticking
public static final int MOVE_ITEM_SPEED = 8;
public static final int HOPPER_CONTAINER_SIZE = 5;
private static final int[][] CACHED_SLOTS = new int[54][];
@@ -71,6 +71,58 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
this.maxStack = size;
}
// CraftBukkit end
+ // Sakura start - optimise hopper ticking
+ private static final int SOURCE_CONTAINER = 1 << 0;
+ private static final int ATTACHED_CONTAINER = 1 << 1;
+ private int connectedContainers = 0;
+
+ @Override
+ public final void neighborChange() {
+ this.startTicking();
+ }
+
+ @Override
+ public final void neighborRemoved() {
+ this.connectedContainers = 0;
+ this.startTicking();
+ }
+
+ private void startTicking() {
+ this.cooldownTime -= this.getIdleTickCount();
+ this.setBlockEntityTicking(true);
+ }
+
+ private void waitForChange(int fullState) {
+ if ((fullState == HOPPER_IS_FULL || (this.connectedContainers & SOURCE_CONTAINER) != 0) && (this.connectedContainers & ATTACHED_CONTAINER) != 0) {
+ this.addListener(this);
+ this.setBlockEntityTicking(false);
+ }
+ }
+
+ private static @Nullable Container sakura_getSourceContainer(Level level, Hopper hopper, BlockPos pos, BlockState state) {
+ Container container = getSourceContainer(level, hopper, pos, state);
+ if (hopper instanceof HopperBlockEntity hbe && org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ hbe.listenForContainerChanges(container, SOURCE_CONTAINER);
+ }
+ return container;
+ }
+
+ private static @Nullable Container sakura_getAttachedContainer(Level level, BlockPos pos, HopperBlockEntity hbe) {
+ Container container = getAttachedContainer(level, pos, hbe);
+ if (org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) {
+ hbe.listenForContainerChanges(container, ATTACHED_CONTAINER);
+ }
+ return container;
+ }
+
+ private void listenForContainerChanges(@Nullable Container container, int type) {
+ if (container != null && container.addListener(this)) {
+ this.connectedContainers |= type; // set
+ } else if ((this.connectedContainers & type) != 0) {
+ this.connectedContainers ^= type; // unset
+ }
+ }
+ // Sakura end - optimise hopper ticking
public HopperBlockEntity(BlockPos pos, BlockState blockState) {
super(BlockEntityType.HOPPER, pos, blockState);
@@ -200,6 +252,12 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
setChanged(level, pos, state);
return true;
}
+
+ // Sakura start - optimise hopper ticking
+ if (level.sakuraConfig().technical.optimiseIdleHopperTicking) {
+ blockEntity.waitForChange(fullState);
+ }
+ // Sakura end - optimise hopper ticking
}
return false;
@@ -417,7 +475,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper end - Perf: Optimize Hoppers
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
- Container attachedContainer = getAttachedContainer(level, pos, blockEntity);
+ Container attachedContainer = sakura_getAttachedContainer(level, pos, blockEntity); // Sakura - optimise hopper ticking
if (attachedContainer == null) {
return false;
} else {
@@ -530,7 +588,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean suckInItems(Level level, Hopper hopper) {
BlockPos blockPos = BlockPos.containing(hopper.getLevelX(), hopper.getLevelY() + 1.0, hopper.getLevelZ());
BlockState blockState = level.getBlockState(blockPos);
- Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState);
+ Container sourceContainer = sakura_getSourceContainer(level, hopper, blockPos, blockState); // Sakura - optimise hopper ticking
if (sourceContainer != null) {
Direction direction = Direction.DOWN;
skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers
diff --git a/net/minecraft/world/level/block/entity/TickingBlockEntity.java b/net/minecraft/world/level/block/entity/TickingBlockEntity.java
index 28e3b73507b988f7234cbf29c4024c88180d0aef..a0d247aa883553708c4b92158232425593d50534 100644
--- a/net/minecraft/world/level/block/entity/TickingBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/TickingBlockEntity.java
@@ -10,4 +10,10 @@ public interface TickingBlockEntity {
BlockPos getPos();
String getType();
+
+ // Sakura start - optimise hopper ticking
+ default boolean isBlockEntityActive() {
+ return true;
+ }
+ // Sakura end - optimise hopper ticking
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index cfc125d43cf3702d46c221f4e7b0d66a15c5d690..9f1a944b82051ea957a0fe725ca95b8f26491f1d 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -980,6 +980,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
return BlockEntityType.getKey(this.blockEntity.getType()).toString();
}
+ // Sakura start - optimise hopper ticking
+ @Override
+ public boolean isBlockEntityActive() {
+ return this.blockEntity.isBlockEntityActive();
+ }
+ // Sakura end - optimise hopper ticking
+
@Override
public String toString() {
return "Level ticker for " + this.getType() + "@" + this.getPos();
@@ -1028,6 +1035,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
return this.ticker.getType();
}
+ // Sakura start - optimise hopper ticking
+ @Override
+ public boolean isBlockEntityActive() {
+ return this.ticker.isBlockEntityActive();
+ }
+ // Sakura end - optimise hopper ticking
+
@Override
public String toString() {
return this.ticker + " <wrapped>";