9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-28 11:19:08 +00:00

Store block change listeners in chunk sections

This commit is contained in:
Samsuik
2025-10-12 20:33:22 +01:00
parent 58356180bf
commit b6f120fc0c
5 changed files with 102 additions and 57 deletions

View File

@@ -32,49 +32,75 @@ index 24f43dd7d3ffa060409c96882c0416f59dc571cc..4b2ea3c99bcd5452840b26c2ba607a4f
protected Level(
WritableLevelData levelData,
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 4dcec2e8a3120a3dfa078e8cf6857ba99ca01a2d..4a274798077c4ee0a504a784449a1aef1d89d325 100644
index 4dcec2e8a3120a3dfa078e8cf6857ba99ca01a2d..c7454fc77f7b269608edf6d6f3e39d9d96e037a0 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -136,6 +136,21 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -136,6 +136,16 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
return this.getBlockStateFinal(x, y, z);
}
// Paper end - get block chunk optimisation
+ // Sakura start - track block changes and tick scheduler
+ private java.util.List<me.samsuik.sakura.listener.BlockChangeTracker.Listener> blockChangeListeners;
+
+ public final void updateBlockChangeListeners(final java.util.List<me.samsuik.sakura.listener.BlockChangeTracker.Listener> listeners) {
+ this.blockChangeListeners = listeners;
+ }
+
+ private void blockChange(final BlockPos pos, final BlockState newBlock, final BlockState oldBlock) {
+ for (final me.samsuik.sakura.listener.BlockChangeTracker.Listener listener : this.blockChangeListeners) {
+ if (listener.test(this.level, pos, newBlock, oldBlock)) {
+ listener.call();
+ public final void updateBlockChangeListeners(final it.unimi.dsi.fastutil.ints.Int2ObjectMap<List<me.samsuik.sakura.listener.BlockChangeTracker.Listener>> chunkSectionListeners) {
+ chunkSectionListeners.forEach((pos, listeners) -> {
+ final int sectionIndex = this.getSectionIndexFromSectionY(pos);
+ if (sectionIndex >= 0 && sectionIndex < this.sections.length) {
+ this.sections[sectionIndex].setBlockChangeListeners(listeners);
+ }
+ }
+ });
+ }
+ // Sakura end - track block changes and tick scheduler
public LevelChunk(Level level, ChunkPos pos) {
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
@@ -173,6 +188,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -173,6 +183,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
this.debug = !empty && this.level.isDebug();
this.defaultBlockState = empty ? VOID_AIR_BLOCKSTATE : AIR_BLOCKSTATE;
// Paper end - get block chunk optimisation
+ this.blockChangeListeners = level.blockChangeTracker.getListenersForChunk(pos); // Sakura - track block changes and tick scheduler
+ this.updateBlockChangeListeners(level.blockChangeTracker.getListenersForChunk(pos, this.minSection, this.maxSection)); // Sakura - track block changes and tick scheduler
}
public LevelChunk(ServerLevel level, ProtoChunk chunk, @Nullable LevelChunk.PostLoadProcessor postLoad) {
@@ -421,6 +437,12 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -421,6 +432,12 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
if (!section.getBlockState(i, i1, i2).is(block)) {
return null;
} else {
+ // Sakura start - track block changes and tick scheduler
+ if (state.getBlock() != blockState.getBlock()) {
+ this.blockChange(pos, state, blockState);
+ section.blockChange(this.level, pos, state, blockState);
+ }
+ // Sakura end - track block changes and tick scheduler
+
if (!this.level.isClientSide() && (flags & 512) == 0 && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled.
state.onPlace(this.level, pos, blockState, flag1);
}
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 029d224557613b46f015785a5bbffe49a6f39ec6..512835828f65f496c730122091d9bd117ca5eb78 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -43,6 +43,26 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
return this.tickingBlocks;
}
// Paper end - block counting
+ // Sakura start - track block changes and tick scheduler
+ private java.util.List<me.samsuik.sakura.listener.BlockChangeTracker.Listener> blockChangeListeners = java.util.List.of();
+
+ public void setBlockChangeListeners(final java.util.List<me.samsuik.sakura.listener.BlockChangeTracker.Listener> listeners) {
+ this.blockChangeListeners = listeners;
+ }
+
+ public void blockChange(
+ final net.minecraft.world.level.Level level,
+ final net.minecraft.core.BlockPos pos,
+ final BlockState newBlock,
+ final BlockState oldBlock
+ ) {
+ for (final me.samsuik.sakura.listener.BlockChangeTracker.Listener listener : this.blockChangeListeners) {
+ if (listener.test(level, pos, newBlock, oldBlock)) {
+ listener.call();
+ }
+ }
+ }
+ // Sakura end - track block changes and tick scheduler
private LevelChunkSection(LevelChunkSection section) {
this.nonEmptyBlockCount = section.nonEmptyBlockCount;

View File

@@ -55,7 +55,7 @@ index 1da42c11174bb7eae9a827a17a57d5e7f1d80f09..7aa5ffc32835fab4a91db464d9112785
// 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 c6bec5967f7792ad3b65c0c69fdafd36194d8823..ad097818384adab7fe4d9e83e2295d20f531a5e3 100644
index 73b602eee0da94f657b4b4cb654147f7ba41c1a4..caa1f6dfafd7466e12efcc99aa447d285b938fe3 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 {
@@ -287,10 +287,10 @@ 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 4a274798077c4ee0a504a784449a1aef1d89d325..4e84ead78c91b3453f2549b010de4aa8691967ba 100644
index c7454fc77f7b269608edf6d6f3e39d9d96e037a0..4b9efc2843bb090871374f0a0bc9d8298760b7ec 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1022,6 +1022,13 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -1017,6 +1017,13 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
return BlockEntityType.getKey(this.blockEntity.getType()).toString();
}
@@ -304,7 +304,7 @@ index 4a274798077c4ee0a504a784449a1aef1d89d325..4e84ead78c91b3453f2549b010de4aa8
@Override
public String toString() {
return "Level ticker for " + this.getType() + "@" + this.getPos();
@@ -1070,6 +1077,13 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -1065,6 +1072,13 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
return this.ticker.getType();
}

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Optimise block counting for cannon entities
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
index 54d561e1b4069df7115457accb0763fa7586dddc..29f66ef8355a95b2869988187bf74536343f2ec8 100644
index 3231c86488806376b086bc26ceba77a509105b81..7bb7eba143f294955eb6b4033b518ea99e428466 100644
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
@@ -1941,6 +1941,7 @@ public final class CollisionUtil {
@@ -42,7 +42,7 @@ index 54d561e1b4069df7115457accb0763fa7586dddc..29f66ef8355a95b2869988187bf74536
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 6804055e9344eb2ea0b2dd6318231963376791c2..3a3d22835df907a85c38cd178c1db9d96554b8e0 100644
index a760757da12210b315baad6c3a3d6f52450e78b2..07352f9ca3e801de5cad99bb31317bc508a011cd 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -621,6 +621,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -79,13 +79,13 @@ index 6804055e9344eb2ea0b2dd6318231963376791c2..3a3d22835df907a85c38cd178c1db9d9
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 029d224557613b46f015785a5bbffe49a6f39ec6..a13771119488202fa0ad27121616f82553be968c 100644
index 512835828f65f496c730122091d9bd117ca5eb78..6699f03354449f01291b1acaf334bfb24729f38a 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -43,6 +43,13 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
return this.tickingBlocks;
@@ -63,6 +63,13 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
}
}
// Paper end - block counting
// Sakura end - track block changes and tick scheduler
+ // Sakura start - optimise block counting for cannon entities
+ private short movingPistonBlocks;
+
@@ -96,7 +96,7 @@ index 029d224557613b46f015785a5bbffe49a6f39ec6..a13771119488202fa0ad27121616f825
private LevelChunkSection(LevelChunkSection section) {
this.nonEmptyBlockCount = section.nonEmptyBlockCount;
@@ -113,6 +120,18 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -133,6 +140,18 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
}
}
@@ -115,7 +115,7 @@ index 029d224557613b46f015785a5bbffe49a6f39ec6..a13771119488202fa0ad27121616f825
final boolean oldTicking = oldState.isRandomlyTicking();
final boolean newTicking = newState.isRandomlyTicking();
if (oldTicking != newTicking) {
@@ -188,6 +207,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -208,6 +227,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
this.tickingBlockCount = (short)0;
this.tickingFluidCount = (short)0;
this.specialCollidingBlocks = (short)0;
@@ -123,7 +123,7 @@ index 029d224557613b46f015785a5bbffe49a6f39ec6..a13771119488202fa0ad27121616f825
this.tickingBlocks.clear();
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
@@ -216,6 +236,12 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -236,6 +256,12 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
continue;
}

View File

@@ -1,9 +1,13 @@
package me.samsuik.sakura.listener;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
@@ -16,7 +20,7 @@ import java.util.function.LongConsumer;
@NullMarked
public final class BlockChangeTracker {
private final Long2ObjectMap<List<Listener>> chunkListeners = new Long2ObjectOpenHashMap<>();
private final Long2ObjectMap<List<Listener>> chunkSectionListeners = new Long2ObjectOpenHashMap<>();
private final Long2ObjectMap<Listener> identifiersInUse = new Long2ObjectOpenHashMap<>();
private final Level level;
private long identifier = Long.MIN_VALUE;
@@ -26,7 +30,7 @@ public final class BlockChangeTracker {
}
public long listenForChangesOnce(final BlockChangeFilter filter, final Set<BlockPos> positions, final Runnable callback) {
final LongConsumer singleUseCallback = (identifier) -> {
final LongConsumer singleUseCallback = identifier -> {
callback.run();
this.stopListening(identifier);
};
@@ -36,9 +40,11 @@ public final class BlockChangeTracker {
public long listenForChanges(final BlockChangeFilter filter, final Set<BlockPos> positions, final LongConsumer callback) {
final long identifier = this.identifier++;
final Listener listener = new Listener(filter, positions, identifier, callback);
for (final ChunkPos chunkPos : getChunkPositions(positions)) {
this.addListenerToChunk(chunkPos, listener);
for (final long sectionPos : getSectionPositions(positions)) {
this.addListenerToSection(sectionPos, listener);
}
this.identifiersInUse.put(identifier, listener);
return identifier;
}
@@ -47,45 +53,57 @@ public final class BlockChangeTracker {
final Listener listener = this.identifiersInUse.remove(identifier);
//noinspection ConstantValue
if (listener != null) {
for (final ChunkPos chunkPos : getChunkPositions(listener.positions())) {
this.removeListenerFronChunk(chunkPos, listener);
for (final long sectionPos : getSectionPositions(listener.positions())) {
this.removeListenerFromSection(sectionPos, listener);
}
}
}
private void removeListenerFronChunk(final ChunkPos chunkPos, final Listener listener) {
final long chunkKey = chunkPos.toLong();
final List<Listener> listeners = this.chunkListeners.computeIfPresent(chunkKey, (k, present) -> {
private static LongOpenHashSet getSectionPositions(final Set<BlockPos> positions) {
final LongOpenHashSet sections = new LongOpenHashSet();
for (final BlockPos pos : positions) {
sections.add(SectionPos.asLong(pos));
}
return sections;
}
private void removeListenerFromSection(final long sectionPos, final Listener listener) {
final List<Listener> listeners = this.chunkSectionListeners.computeIfPresent(sectionPos, (k, present) -> {
present.remove(listener);
return present.isEmpty() ? null : present;
});
this.updateListeners(chunkPos, Objects.requireNonNullElse(listeners, Collections.emptyList()));
this.updateListeners(sectionPos, Objects.requireNonNullElse(listeners, Collections.emptyList()));
}
private void addListenerToChunk(final ChunkPos chunkPos, final Listener listener) {
final long chunkKey = chunkPos.toLong();
final List<Listener> listeners = this.chunkListeners.computeIfAbsent(chunkKey, i -> new ArrayList<>());
private void addListenerToSection(final long sectionPos, final Listener listener) {
final List<Listener> listeners = this.chunkSectionListeners.computeIfAbsent(sectionPos, k -> new ArrayList<>());
listeners.add(listener);
this.updateListeners(chunkPos, listeners);
this.updateListeners(sectionPos, listeners);
}
private void updateListeners(final ChunkPos chunkPos, final List<Listener> listeners) {
final LevelChunk chunk = ((ServerLevel) this.level).chunkSource.getChunkAtIfLoadedImmediately(chunkPos.x, chunkPos.z);
private void updateListeners(final long sectionPos, final List<Listener> listeners) {
final int chunkX = SectionPos.x(sectionPos);
final int chunkZ = SectionPos.x(sectionPos);
final LevelChunk chunk = ((ServerLevel) this.level).chunkSource.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (chunk != null) {
chunk.updateBlockChangeListeners(List.copyOf(listeners));
final Int2ObjectMap<List<Listener>> sectionListeners = Int2ObjectMaps.singleton(
SectionPos.y(sectionPos),
List.copyOf(listeners)
);
chunk.updateBlockChangeListeners(sectionListeners);
}
}
public List<Listener> getListenersForChunk(final ChunkPos chunkPos) {
return List.copyOf(this.chunkListeners.getOrDefault(chunkPos.toLong(), Collections.emptyList()));
}
private static Set<ChunkPos> getChunkPositions(final Set<BlockPos> positions) {
final Set<ChunkPos> chunkPositions = new ObjectOpenHashSet<>();
for (final BlockPos pos : positions) {
chunkPositions.add(new ChunkPos(pos));
public Int2ObjectMap<List<Listener>> getListenersForChunk(final ChunkPos chunkPos, final int minSection, final int maxSection) {
final Int2ObjectOpenHashMap<List<Listener>> sectionListeners = new Int2ObjectOpenHashMap<>();
for (int sectionY = minSection; sectionY <= maxSection; ++sectionY) {
final long sectionPos = SectionPos.asLong(chunkPos.x, sectionY, chunkPos.z);
final List<Listener> listeners = this.chunkSectionListeners.getOrDefault(sectionPos, Collections.emptyList());
sectionListeners.put(sectionY, List.copyOf(listeners));
}
return chunkPositions;
return sectionListeners;
}
public interface BlockChangeFilter {

View File

@@ -67,6 +67,7 @@ public final class LevelTickScheduler {
if (tick > gameTime) {
continue;
}
final List<TickTask> tasks = this.scheduledTasks.remove(tick);
this.runTasks(tasks, gameTime);
this.removeLater.addAll(tasks);