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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user