9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-19 14:59:30 +00:00

Reimplement vanilla and eigencraft redstone cache

This commit is contained in:
Samsuik
2025-02-16 23:29:18 +00:00
parent 047821fb7d
commit 4fa7c85bce
22 changed files with 608 additions and 25 deletions

View File

@@ -18,5 +18,7 @@ protected-f net.minecraft.world.entity.Entity eyeHeight
protected-f net.minecraft.world.level.ServerExplosion center
public net.minecraft.server.level.ServerLevel entityTickList
public net.minecraft.world.entity.Entity stuckSpeedMultiplier
public net.minecraft.world.level.Level neighborUpdater
public net.minecraft.world.level.block.RedStoneWireBlock turbo
public net.minecraft.world.level.entity.EntityTickList entities
public net.minecraft.world.level.material.FlowingFluid getLegacyLevel(Lnet/minecraft/world/level/material/FluidState;)I

View File

@@ -17,7 +17,7 @@ index 3a926d4d34a7c68a24b9e00bcbcff271c8992ad2..259c2b2d459d86cb11ab848c77f48c38
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 38fc85d18f737736dcdf5142c8357f9af43e4d19..1165cf61413e9a1cd4373ef5f5858e8c5a040a20 100644
index 316dca7b4b20c6d05bf76f64a79984d10807dab5..9fbce0f31a73928a7db752e085fb3d0020fc6387 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -837,6 +837,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -32,7 +32,7 @@ index 38fc85d18f737736dcdf5142c8357f9af43e4d19..1165cf61413e9a1cd4373ef5f5858e8c
protected Level(
WritableLevelData levelData,
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..7887799c39b6e5b1657aff49d7110cf14bec8a79 100644
index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..2fb04fa82cc7b726df1e42a1cf452ebfbdd4503a 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -126,6 +126,21 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
@@ -48,7 +48,7 @@ index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..7887799c39b6e5b1657aff49d7110cf1
+
+ private void blockChange(BlockPos pos, BlockState newBlock, BlockState oldBlock) {
+ for (me.samsuik.sakura.listener.BlockChangeTracker.Listener listener : this.blockChangeListeners) {
+ if (listener.test(pos, newBlock, oldBlock)) {
+ if (listener.test(this.level, pos, newBlock, oldBlock)) {
+ listener.call();
+ }
+ }

View File

@@ -35,7 +35,7 @@ index 5e48d84c5ab335975f6533472aec1e1789375bbf..30e6492e60fc7b5767d09574304ad3a3
public Entity(EntityType<?> entityType, Level level) {
this.type = entityType;
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 1165cf61413e9a1cd4373ef5f5858e8c5a040a20..5c61a690327cd1c872cdb3604cfa89d988918360 100644
index 9fbce0f31a73928a7db752e085fb3d0020fc6387..869670abd843d6c10babf8ffaa7bfc919f8a818a 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1514,6 +1514,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl

View File

@@ -276,7 +276,7 @@ index b6467f9ff64a76104076a01bfd56852ce712d95b..e6aa1d1a5fc5cd2ffc156125c4eef2d0
@Nullable
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 8688f08fa902445807fe018c44e4a3b3ca01c55e..0db34fe552d65145e5714b4a9adec20c119384e7 100644
index 869670abd843d6c10babf8ffaa7bfc919f8a818a..5c4c2823ec6b5e2d9293b9130ddf2bc1c3c12048 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -841,6 +841,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl

View File

@@ -17,7 +17,7 @@ index 8fec4a46a9cf13d832306bd87a4fe17be79bef86..dce3331c7dbbf9bb6c6342d0a760c040
// Paper start
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 410a086f2fe2b6f695dd9bcce5a1f064b239f5c2..4314234e1d7f7a0fedf0ecf29ac3284e51be88d7 100644
index 5c4c2823ec6b5e2d9293b9130ddf2bc1c3c12048..125f61e6560790bed25e2f7aedaa189caf31a8aa 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -842,6 +842,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -114,7 +114,7 @@ index 69d490c79e30fb42da69bbd804ecaea7b88fe7b0..497409e04dc4b9366da1fbe0641b8424
this.updateNeighbours(level, pos);
level.setBlocksDirty(pos, state, blockState);
diff --git a/net/minecraft/world/level/block/TripWireHookBlock.java b/net/minecraft/world/level/block/TripWireHookBlock.java
index 6a7e5a642e2eaf7d5dffadb81738f7385a38c0af..dd2ceb8fd24281a6dd06084145f387cff55c6cc9 100644
index 30b97cdcd495490ef65c2ab9dfc39a39c93002ca..e57cc9f0987d9d4cda888bc633e5f24e510ec709 100644
--- a/net/minecraft/world/level/block/TripWireHookBlock.java
+++ b/net/minecraft/world/level/block/TripWireHookBlock.java
@@ -182,6 +182,11 @@ public class TripWireHookBlock extends Block {

View File

@@ -83,7 +83,7 @@ index 7554c109c35397bc1a43dd80e87764fd78645bbf..d60f30f7afb15cc90c1bd4b816136d00
}
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 4314234e1d7f7a0fedf0ecf29ac3284e51be88d7..544617311ccc2573273cebba62e9503caa8a8dc3 100644
index 125f61e6560790bed25e2f7aedaa189caf31a8aa..dc3c1d354bd4fb557f295c8dca14a31048def456 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1795,10 +1795,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl

View File

@@ -41,7 +41,7 @@ index 68e50c6ade879d263424f244070677cb81c34c33..8467af4ee57b6699227370ada7bf15ca
return !interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.CONSUMABLE)
? super.use(context.getLevel(), context.getPlayer(), context.getHand())
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 544617311ccc2573273cebba62e9503caa8a8dc3..7d98febf87a51fb7d9b502fa362d1bc4ac1e3eaa 100644
index dc3c1d354bd4fb557f295c8dca14a31048def456..f7a341082d78667d34e7832f4567bbf4f3f86a00 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -843,6 +843,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl

View File

@@ -676,7 +676,7 @@ index 78aa0ae9ce937b7232eac1d65fd987c21489979d..e719be50ff73610046696a2105367133
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState())) {
this.fizz(level, pos);
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index efdcee032174af1f9a14183e9e8af3e7c4694942..d763a636ecb92a12786f49e2096a95827a3d1717 100644
index 945f34971680bb1c90ea133431c900ad1b5a8ced..31a76dc1ee4a02ad91637b27d1927b5a5d38ddb3 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -572,6 +572,10 @@ public class RedStoneWireBlock extends Block {

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Allow explosions to destroy lava
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index a4ceca6cfaaf048f3f78b18724d54780675354e7..0ed83fa6b8c8c38cc9f94a381276d300abdac839 100644
index 8aa4debbc68530670ba6329554da5e9cf8e64a71..5d8d59fa34c9070d27e3ff38922eee465f03e663 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -407,6 +407,11 @@ public class ServerExplosion implements Explosion {

View File

@@ -46,7 +46,7 @@ index 47c8ed946cb2ad81a4469daf60dabc40c5e8beda..16b66b19157081c7717f73ee3dc91116
mutablePos.set(blockX, blockY, blockZ);
blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape);
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index c5b840f990f9f357d4b63f7e450e91c431fd2d03..e72c6eed0280aab425ae1dfa1d104a2d87651b63 100644
index 661f913bfa8a8bd0c4929a79745ea2e0f45098e4..a80fd65ea35590a992c6340a2f42f578afdaf1ab 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -537,6 +537,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Protect scaffolding from creepers
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 0ed83fa6b8c8c38cc9f94a381276d300abdac839..b43487bd468179a9634897002df44146e9f24c1b 100644
index 5d8d59fa34c9070d27e3ff38922eee465f03e663..95fecb0338bc9c7b6c2ca484994f2f72370c5822 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -412,6 +412,11 @@ public class ServerExplosion implements Explosion {

View File

@@ -42,7 +42,7 @@ index 2d3721e311851c1801b090e99d4f9d0daf4e5f99..2249f5338f97471a833acddcee95f6a7
boolean isEmpty();
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 7d98febf87a51fb7d9b502fa362d1bc4ac1e3eaa..cfbec037f4314d7425e4abf14c77e57752eb2ae3 100644
index f7a341082d78667d34e7832f4567bbf4f3f86a00..850de480eb3408b3a4468266aa7d16a09332b237 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1499,7 +1499,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -282,7 +282,7 @@ index 28e3b73507b988f7234cbf29c4024c88180d0aef..a0d247aa883553708c4b921582324255
+ // Sakura end - optimise hopper ticking
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 7887799c39b6e5b1657aff49d7110cf14bec8a79..f71ae8ebf7ce494b2abf350513f826139c18eadc 100644
index 2fb04fa82cc7b726df1e42a1cf452ebfbdd4503a..e2b684c630a44d7f84790ad305593823edb241ca 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -972,6 +972,13 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p

View File

@@ -0,0 +1,196 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Sun, 16 Feb 2025 23:27:59 +0000
Subject: [PATCH] Cache vanilla and eigencraft redstone wires
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..d90f6aa4557b5863eba6a206226f763c1e89dc31 100644
--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
@@ -658,6 +658,7 @@ public final class RedstoneWireTurbo {
// while these updates are dispatched to non-wires only, so we can
// pass null.
worldIn.getBlockState(upd.self).handleNeighborChanged(worldIn, upd.self, wire, null, false);
+ worldIn.redstoneWireCache.trackNeighbor(upd.self); // Sakura - cache vanilla and eigencraft wires
}
}
@@ -801,6 +802,7 @@ public final class RedstoneWireTurbo {
// Perform the walk over all directly reachable redstone wire blocks, propagating wire value
// updates in a breadth first order out from the initial update received for the block at 'pos'.
breadthFirstWalk(worldIn);
+ worldIn.redstoneWireCache.stopTracking(); // Sakura - cache vanilla and eigencraft wires
// With the whole search completed, clear the list of all known blocks.
// We do not want to keep around state information that may be changed by other code.
@@ -909,6 +911,7 @@ public final class RedstoneWireTurbo {
// bypass the new neighbor update stack.
if (worldIn.setBlock(upd.self, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS))
updateNeighborShapes(worldIn, upd.self, state);
+ worldIn.redstoneWireCache.trackWirePower(upd.self, j, i); // Sakura - cache vanilla and eigencraft wires
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 539c2e465d4c89584b5bccaad18fadc41db0643a..ca21ac80c07f6aeecf01791d0eb7b5236063a0ed 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -699,6 +699,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.levelTickScheduler.registerNewTask(this.explosionPositions::clear, 0); // Sakura - client visibility settings
this.levelTickScheduler.registerNewTask(this.mergeHandler::expire, 200); // Sakura - merge cannon entities
this.levelTickScheduler.registerNewTask(this.densityCache::invalidate, 0); // Sakura - explosion density cache
+ this.levelTickScheduler.registerNewTask(this.redstoneWireCache::expire, 300); // Sakura - cache vanilla and eigencraft wires
}
// Paper start
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 850de480eb3408b3a4468266aa7d16a09332b237..56161f32e9fb379fc207b0ef2c1ba44e9516b32f 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -844,6 +844,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities
public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache
public final me.samsuik.sakura.explosion.durable.DurableBlockManager durabilityManager = new me.samsuik.sakura.explosion.durable.DurableBlockManager(); // Sakura - explosion durable blocks
+ public final me.samsuik.sakura.redstone.RedstoneWireCache redstoneWireCache = new me.samsuik.sakura.redstone.RedstoneWireCache(this); // Sakura - cache vanilla and eigencraft wires
protected Level(
WritableLevelData levelData,
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 31a76dc1ee4a02ad91637b27d1927b5a5d38ddb3..f1f6159fd097db461032b8bcfdaa8bc761fbf341 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -331,6 +331,12 @@ public class RedStoneWireBlock extends Block {
newPower = event.getNewCurrent();
+ // Sakura start - cache vanilla and eigencraft wires
+ if (level.redstoneWireCache.tryApplyFromCache(pos, null, newPower, oldPower)) {
+ return state;
+ }
+ // Sakura end - cache vanilla and eigencraft wires
+
if (level.getBlockState(pos) == state) {
state = state.setValue(POWER, newPower);
// [Space Walker] suppress shape updates and emit those manually to
@@ -338,6 +344,7 @@ public class RedStoneWireBlock extends Block {
if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) {
turbo.updateNeighborShapes(level, pos, state);
}
+ level.redstoneWireCache.trackWirePower(pos, newPower, oldPower); // Sakura - cache vanilla and eigencraft wires
}
}
return state;
@@ -460,8 +467,14 @@ public class RedStoneWireBlock extends Block {
if (powerValue == 0) {
return 0;
} else {
- return side != Direction.UP
- && !this.getConnectionState(blockAccess, blockState, pos).getValue(PROPERTY_BY_DIRECTION.get(side.getOpposite())).isConnected()
+ // Sakura start - cache vanilla and eigencraft wires
+ if (side == Direction.UP) {
+ return powerValue;
+ }
+ final boolean updating = blockAccess instanceof Level level && level.redstoneWireCache.isWireUpdating(pos);
+ final BlockState state = updating ? blockState : this.getConnectionState(blockAccess, blockState, pos);
+ return !state.getValue(PROPERTY_BY_DIRECTION.get(side.getOpposite())).isConnected()
+ // Sakura end - cache vanilla and eigencraft wires
? 0
: powerValue;
}
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index 0a233c18fbad92c59b9c001574be3464f2be9d2c..80db5829a8b023ffbc25341168466a398346e1b8 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -524,6 +524,13 @@ public abstract class BlockBehaviour implements FeatureElement {
return this.constantCollisionShape;
}
// Paper end - optimise collisions
+ // Sakura start - cache vanilla and eigencraft wires
+ private boolean specialBlock;
+
+ public final boolean isSpecialBlock() {
+ return this.specialBlock;
+ }
+ // Sakura end - cache vanilla and eigencraft wires
protected BlockStateBase(Block owner, Reference2ObjectArrayMap<Property<?>, Comparable<?>> values, MapCodec<BlockState> propertiesCodec) {
super(owner, values, propertiesCodec);
@@ -638,6 +645,7 @@ public abstract class BlockBehaviour implements FeatureElement {
}
}
// Paper end - optimise collisions
+ this.specialBlock = !Block.class.equals(this.owner.getClass()); // Sakura - cache vanilla and eigencraft wires
}
public Block getBlock() {
diff --git a/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
index 028eae2f9a459b60e92f3344091083aa93b54485..9fbf679b54088f89ac4ba727ccb645d645f01bd7 100644
--- a/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
+++ b/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
@@ -26,6 +26,13 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
this.maxChainedNeighborUpdates = maxChainedNeighborUpdates;
}
+ // Sakura start - cache vanilla and eigencraft wires
+ @Override
+ public final int getUpdateDepth() {
+ return this.count;
+ }
+ // Sakura end - cache vanilla and eigencraft wires
+
@Override
public void shapeUpdate(Direction direction, BlockState state, BlockPos pos, BlockPos neighborPos, int flags, int recursionLevel) {
this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, state, pos.immutable(), neighborPos.immutable(), flags, recursionLevel));
@@ -83,6 +90,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
}
}
} finally {
+ this.level.redstoneWireCache.stopTracking(); // Sakura - cache vanilla and eigencraft wires
this.stack.clear();
this.addedThisLayer.clear();
this.count = 0;
diff --git a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
index 2d77780b6727f82ffc3cb216ca5f2d6483496cfd..981ec4e417d07a475a19032b0efe2e188e2e7180 100644
--- a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
+++ b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java
@@ -27,7 +27,14 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
}
if (oldPower != i) {
// CraftBukkit end
+ // Sakura start - cache vanilla and eigencraft wires
+ final me.samsuik.sakura.redstone.RedstoneWireCache wireCache = level.redstoneWireCache;
+ if (wireCache.tryApplyFromCache(pos, orientation, i, oldPower)) {
+ return;
+ }
if (level.getBlockState(pos) == state) {
+ wireCache.trackWirePower(pos, i, oldPower);
+ // Sakura end - cache vanilla and eigencraft wires
level.setBlock(pos, state.setValue(RedStoneWireBlock.POWER, Integer.valueOf(i)), 2);
}
@@ -39,6 +46,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator {
}
for (BlockPos blockPos : set) {
+ wireCache.trackNeighborsAt(blockPos); // Sakura - cache vanilla and eigencraft wires
level.updateNeighborsAt(blockPos, this.wireBlock);
}
}
diff --git a/net/minecraft/world/level/redstone/NeighborUpdater.java b/net/minecraft/world/level/redstone/NeighborUpdater.java
index 26c15c60d358273a3b369c286771c81d6f0979dd..485a5a5df6f94ea9793cc4ac8141679fa8527532 100644
--- a/net/minecraft/world/level/redstone/NeighborUpdater.java
+++ b/net/minecraft/world/level/redstone/NeighborUpdater.java
@@ -17,6 +17,12 @@ import net.minecraft.world.level.block.state.BlockState;
public interface NeighborUpdater {
Direction[] UPDATE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH};
+ // Sakura start - cache vanilla and eigencraft wires
+ default int getUpdateDepth() {
+ return 0;
+ }
+ // Sakura end - cache vanilla and eigencraft wires
+
void shapeUpdate(Direction direction, BlockState state, BlockPos pos, BlockPos neighborPos, int flags, int recursionLevel);
void neighborChanged(BlockPos pos, Block neighborBlock, @Nullable Orientation orientation);

View File

@@ -1,5 +1,14 @@
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -118,7 +_,7 @@
public final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList(); // Paper - public
public final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
- private boolean tickingBlockEntities;
+ public boolean tickingBlockEntities;
public final Thread thread;
private final boolean isDebug;
private int skyDarken;
@@ -168,6 +_,18 @@
return this.paperConfig;
}

View File

@@ -14,7 +14,6 @@ import me.samsuik.sakura.physics.PhysicsVersion;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;

View File

@@ -165,7 +165,7 @@ public final class LocalConfigManager implements LocalStorageHandler {
public synchronized LocalValueConfig config(BlockPos position) {
long gameTime = this.level.getGameTime();
long ticks = this.expirationTick - gameTime;
long ticks = gameTime - this.expirationTick;
if (ticks >= CONFIG_CACHE_EXPIRATION / 3) {
this.chunkConfigCache.values().removeIf(pair -> pair.value().isExpired(gameTime));
this.expirationTick = gameTime;

View File

@@ -12,7 +12,6 @@ import net.minecraft.world.level.chunk.LevelChunk;
import org.jspecify.annotations.NullMarked;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.LongConsumer;
@NullMarked
@@ -26,7 +25,7 @@ public final class BlockChangeTracker {
this.level = level;
}
public long listenForChangesOnce(BiPredicate<BlockState, BlockState> filter, Set<BlockPos> positions, Runnable callback) {
public long listenForChangesOnce(BlockChangeFilter filter, Set<BlockPos> positions, Runnable callback) {
LongConsumer singleUseCallback = (identifier) -> {
callback.run();
this.stopListening(identifier);
@@ -34,7 +33,7 @@ public final class BlockChangeTracker {
return this.listenForChanges(filter, positions, singleUseCallback);
}
public long listenForChanges(BiPredicate<BlockState, BlockState> filter, Set<BlockPos> positions, LongConsumer callback) {
public long listenForChanges(BlockChangeFilter filter, Set<BlockPos> positions, LongConsumer callback) {
long identifier = this.identifier++;
Listener listener = new Listener(
filter, positions, identifier, callback
@@ -48,10 +47,12 @@ public final class BlockChangeTracker {
public void stopListening(long identifier) {
Listener listener = this.identifiersInUse.remove(identifier);
if (listener != null) {
for (ChunkPos chunkPos : getChunkPositions(listener.positions())) {
this.removeListenerFronChunk(chunkPos, listener);
}
}
}
private void removeListenerFronChunk(ChunkPos chunkPos, Listener listener) {
long chunkKey = chunkPos.toLong();
@@ -88,14 +89,18 @@ public final class BlockChangeTracker {
return chunkPositions;
}
public record Listener(BiPredicate<BlockState, BlockState> filter, Set<BlockPos> positions,
public interface BlockChangeFilter {
boolean test(Level level, BlockPos pos, BlockState newBlock, BlockState oldBlock);
}
public record Listener(BlockChangeFilter filter, Set<BlockPos> positions,
long identifier, LongConsumer callback) {
public void call() {
this.callback.accept(this.identifier);
}
public boolean test(BlockPos pos, BlockState newBlock, BlockState oldBlock) {
return this.filter.test(newBlock, oldBlock) && this.positions.contains(pos);
public boolean test(Level level, BlockPos pos, BlockState newBlock, BlockState oldBlock) {
return this.filter.test(level, pos, newBlock, oldBlock) && this.positions.contains(pos);
}
}
}

View File

@@ -0,0 +1,185 @@
package me.samsuik.sakura.redstone;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.*;
import me.samsuik.sakura.utils.TickExpiry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
import net.minecraft.world.level.redstone.NeighborUpdater;
import net.minecraft.world.level.redstone.Orientation;
import org.jspecify.annotations.NullMarked;
import java.util.BitSet;
import java.util.List;
@NullMarked
public final class RedstoneNetwork {
private final List<RedstoneWireUpdate> wireUpdates;
private final List<BlockPos> updates;
private final Object2ObjectMap<BlockPos, RedstoneOriginalPower> originalWirePower;
private final LongArrayList listeners = new LongArrayList();
private final BitSet redundantUpdates = new BitSet();
private final TickExpiry expiry;
public RedstoneNetwork(List<RedstoneWireUpdate> wireUpdates, List<BlockPos> updates, Object2ObjectMap<BlockPos, RedstoneOriginalPower> originalWirePower, TickExpiry expiry) {
this.wireUpdates = new ObjectArrayList<>(wireUpdates);
this.updates = new ObjectArrayList<>(updates);
this.originalWirePower = new Object2ObjectOpenHashMap<>(originalWirePower);
this.expiry = expiry;
}
private static boolean hasRedstoneComponentChanged(Level level, BlockPos pos, BlockState newBlock, BlockState oldBlock) {
return newBlock.isRedstoneConductor(level, pos) != oldBlock.isRedstoneConductor(level, pos)
|| newBlock.isSignalSource() != oldBlock.isSignalSource();
}
private static boolean hasBlockChanged(Level level, BlockPos pos, BlockState newBlock, BlockState oldBlock) {
return newBlock.getBlock() != oldBlock.getBlock();
}
public TickExpiry getExpiry() {
return this.expiry;
}
private boolean isRegistered() {
return !this.listeners.isEmpty();
}
public boolean hasWire(BlockPos pos) {
return this.originalWirePower.containsKey(pos);
}
public void invalidate(Level level) {
for (long identifier : this.listeners) {
level.blockChangeTracker.stopListening(identifier);
}
this.listeners.clear();
}
private void markNeighboringWiresForShapeUpdates(BlockPos pos, Object2ObjectMap<BlockPos, RedstoneWireUpdate> wires) {
for (Direction direction : NeighborUpdater.UPDATE_ORDER) {
BlockPos neighborPos = pos.relative(direction);
RedstoneWireUpdate wireUpdate = wires.get(neighborPos);
//noinspection ConstantValue
if (wireUpdate != null) {
wireUpdate.updateShapes();
}
}
}
public boolean prepareAndRegister(Level level) {
Object2ObjectLinkedOpenHashMap<BlockPos, RedstoneWireUpdate> processedWires = new Object2ObjectLinkedOpenHashMap<>();
for (RedstoneWireUpdate wireUpdate : this.wireUpdates.reversed()) {
BlockPos wirePos = wireUpdate.getPosition();
//noinspection ConstantValue
if (processedWires.putAndMoveToFirst(wirePos, wireUpdate) == null) {
// It's possible for the block below the redstone to break while the network is updating
BlockState state = level.getBlockState(wirePos);
if (state.is(Blocks.PISTON_HEAD)) {
return false;
}
} else if (this.originalWirePower.get(wirePos).firstPower() != wireUpdate.getPower()) {
// Filter out wires updates that are not the first and last update
// This significantly reduces the amount of updates when unpowering
wireUpdate.skipWireUpdate();
}
}
for (int i = 0; i < this.updates.size(); ++i) {
BlockPos updatePos = this.updates.get(i);
BlockState state = level.getBlockState(updatePos);
// Filter out redundant neighbor updates
if (state.isAir() || state.liquid() || !state.isSpecialBlock() || processedWires.containsKey(updatePos)) {
this.redundantUpdates.set(i);
}
// Look for blocks that actually need shape updates
Block block = state.getBlock();
if (state.is(Blocks.OBSERVER) || state.liquid() || block instanceof FallingBlock || block instanceof LiquidBlockContainer) {
this.markNeighboringWiresForShapeUpdates(updatePos, processedWires);
}
}
this.addBlockListeners(level);
return true;
}
private void addBlockListeners(Level level) {
ObjectOpenHashSet<BlockPos> positions = new ObjectOpenHashSet<>(this.updates);
positions.addAll(this.originalWirePower.keySet());
// Register block change listeners
this.listeners.add(level.blockChangeTracker.listenForChangesOnce(
RedstoneNetwork::hasRedstoneComponentChanged, positions, () -> this.invalidate(level)
));
this.listeners.add(level.blockChangeTracker.listenForChangesOnce(
RedstoneNetwork::hasBlockChanged, positions, this.redundantUpdates::clear
));
}
private boolean verifyWiresInNetwork(Level level) {
for (Object2ObjectMap.Entry<BlockPos, RedstoneOriginalPower> wireEntry : this.originalWirePower.object2ObjectEntrySet()) {
BlockState state = level.getBlockState(wireEntry.getKey());
if (!state.is(Blocks.REDSTONE_WIRE)) {
this.invalidate(level);
return false;
}
if (state.getValue(RedStoneWireBlock.POWER) != wireEntry.getValue().originalPower()) {
return false;
}
}
return true;
}
private void performUpdates(Level level, Orientation orientation, RedStoneWireBlock wireBlock, int updateFrom, int updateTo) {
for (int updateIndex = updateFrom; updateIndex < updateTo; ++updateIndex) {
if (this.redundantUpdates.get(updateIndex)) {
continue;
}
BlockPos updatePos = this.updates.get(updateIndex);
level.getBlockState(updatePos).handleNeighborChanged(level, updatePos, wireBlock, orientation, false);
}
}
public boolean applyFromCache(Level level) {
this.expiry.refresh(level.getGameTime());
if (!this.isRegistered() || !this.verifyWiresInNetwork(level)) {
return false;
}
Orientation orientation = ExperimentalRedstoneUtils.initialOrientation(level, null, null);
RedStoneWireBlock wireBlock = (RedStoneWireBlock) Blocks.REDSTONE_WIRE;
int updateFrom = 0;
for (RedstoneWireUpdate wireUpdate : this.wireUpdates) {
if (wireUpdate.canSkipWireUpdate()) {
updateFrom = wireUpdate.getUpdateIndex();
continue;
}
int updateTo = wireUpdate.getUpdateIndex();
this.performUpdates(level, orientation, wireBlock, updateFrom, updateTo);
updateFrom = updateTo;
BlockPos wirePos = wireUpdate.getPosition();
BlockState state = level.getBlockState(wirePos);
BlockState newState = state.setValue(RedStoneWireBlock.POWER, wireUpdate.getPower());
if (level.setBlock(wirePos, newState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE)) {
if (wireUpdate.needsShapeUpdate()) {
wireBlock.turbo.updateNeighborShapes(level, wirePos, newState);
}
}
}
this.performUpdates(level, orientation, wireBlock, updateFrom, this.updates.size());
return true;
}
}

View File

@@ -0,0 +1,22 @@
package me.samsuik.sakura.redstone;
import io.papermc.paper.configuration.WorldConfiguration;
import me.samsuik.sakura.configuration.local.LocalValueConfig;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.redstone.Orientation;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public record RedstoneNetworkSource(WorldConfiguration.Misc.RedstoneImplementation redstoneImplementation,
BlockPos position, @Nullable Orientation orientation,
int updateDepth, int newPower, int oldPower) {
public static RedstoneNetworkSource createNetworkSource(Level level, BlockPos pos, @Nullable Orientation orientation, int newPower, int oldPower) {
LocalValueConfig localConfig = level.localConfig().config(pos);
WorldConfiguration.Misc.RedstoneImplementation redstoneImplementation = localConfig.redstoneImplementation;
int updateDepth = level.neighborUpdater.getUpdateDepth();
return new RedstoneNetworkSource(redstoneImplementation, pos, orientation, updateDepth, newPower, oldPower);
}
}

View File

@@ -0,0 +1,5 @@
package me.samsuik.sakura.redstone;
public record RedstoneOriginalPower(int originalPower, int firstPower) {
}

View File

@@ -0,0 +1,113 @@
package me.samsuik.sakura.redstone;
import it.unimi.dsi.fastutil.objects.*;
import me.samsuik.sakura.utils.TickExpiry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.redstone.NeighborUpdater;
import net.minecraft.world.level.redstone.Orientation;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.List;
import java.util.Map;
@NullMarked
public final class RedstoneWireCache {
private final Map<RedstoneNetworkSource, RedstoneNetwork> networkCache = new Object2ObjectOpenHashMap<>();
private @Nullable RedstoneNetworkSource networkSource;
private final List<RedstoneWireUpdate> wireUpdates = new ObjectArrayList<>();
private final List<BlockPos> updates = new ObjectArrayList<>();
private final Object2ObjectMap<BlockPos, RedstoneOriginalPower> originalWirePower = new Object2ObjectOpenHashMap<>();
private final Level level;
private @Nullable RedstoneNetwork updatingNetwork;
public RedstoneWireCache(Level level) {
this.level = level;
}
public boolean isWireUpdating(BlockPos pos) {
return this.updatingNetwork != null && this.updatingNetwork.hasWire(pos);
}
private boolean isTrackingWireUpdates() {
return this.networkSource != null;
}
public boolean tryApplyFromCache(BlockPos pos, @Nullable Orientation orientation, int newPower, int oldPower) {
if (!this.isTrackingWireUpdates()) {
if (this.updatingNetwork != null) {
return true;
}
RedstoneNetworkSource networkSource = RedstoneNetworkSource.createNetworkSource(this.level, pos, orientation, newPower, oldPower);
RedstoneNetwork network = this.networkCache.get(networkSource);
if (network != null) {
try {
this.updatingNetwork = network;
return network.applyFromCache(this.level);
} finally {
this.updatingNetwork = null;
}
}
this.networkSource = networkSource;
}
return false;
}
public void trackWirePower(BlockPos pos, int newPower, int oldPower) {
if (this.isTrackingWireUpdates()) {
this.originalWirePower.putIfAbsent(pos, new RedstoneOriginalPower(oldPower, newPower));
this.wireUpdates.add(new RedstoneWireUpdate(pos, newPower, this.updates.size()));
}
}
public void trackNeighbor(BlockPos pos) {
if (this.isTrackingWireUpdates()) {
this.updates.add(pos);
}
}
public void trackNeighborsAt(BlockPos pos) {
if (this.isTrackingWireUpdates()) {
for (Direction neighbor : NeighborUpdater.UPDATE_ORDER) {
this.updates.add(pos.relative(neighbor));
}
}
}
public void expire(long tick) {
this.networkCache.values().removeIf(network -> {
if (network.getExpiry().isExpired(tick)) {
network.invalidate(this.level);
return true;
}
return false;
});
}
public void stopTracking() {
if (!this.isTrackingWireUpdates()) {
return;
}
if (!this.wireUpdates.isEmpty()) {
// Cache expires if it has not been used in 600 ticks
TickExpiry expiration = new TickExpiry(this.level.getGameTime(), 600);
RedstoneNetwork redstoneNetwork = new RedstoneNetwork(
this.wireUpdates, this.updates, this.originalWirePower, expiration
);
if (redstoneNetwork.prepareAndRegister(this.level)) {
this.networkCache.put(this.networkSource, redstoneNetwork);
}
}
this.wireUpdates.clear();
this.updates.clear();
this.originalWirePower.clear();
this.networkSource = null;
}
}

View File

@@ -0,0 +1,47 @@
package me.samsuik.sakura.redstone;
import net.minecraft.core.BlockPos;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class RedstoneWireUpdate {
private final BlockPos position;
private final int power;
private final int updateIndex;
private boolean updateShape;
private boolean skipWire;
public RedstoneWireUpdate(BlockPos position, int power, int updateIndex) {
this.position = position;
this.power = power;
this.updateIndex = updateIndex;
}
public BlockPos getPosition() {
return this.position;
}
public int getPower() {
return this.power;
}
public int getUpdateIndex() {
return this.updateIndex;
}
public boolean needsShapeUpdate() {
return this.updateShape;
}
public void updateShapes() {
this.updateShape = true;
}
public boolean canSkipWireUpdate() {
return this.skipWire;
}
public void skipWireUpdate() {
this.skipWire = true;
}
}