diff --git a/leaf-server/minecraft-patches/features/0295-Reduce-optimiseRandomTick-new-BlockPos-instance-crea.patch b/leaf-server/minecraft-patches/features/0295-Reduce-optimiseRandomTick-new-BlockPos-instance-crea.patch new file mode 100644 index 0000000000..3daf44462d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0295-Reduce-optimiseRandomTick-new-BlockPos-instance-crea.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MrlingXD <90316914+wling-art@users.noreply.github.com> +Date: Thu, 22 May 2025 02:28:50 +0800 +Subject: [PATCH] Reduce optimiseRandomTick new BlockPos instance create + + +diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java +index 0838b3d60e94a280a6fdd1aef413078a0ec71a1c..bdc5f13321362d5ecd99562b48f9950ad72bbfba 100644 +--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java ++++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java +@@ -717,7 +717,7 @@ public final class RedstoneWireTurbo { + upd.parent = pos; + upd.visited = true; + identifyNode(worldIn, upd); +- nodeCache.put(pos, upd); ++ nodeCache.put(pos.immutable(), upd); // Leaf - Reduce optimiseRandomTick new BlockPos instance create - immutable + } + upd.currentState = newState; + +@@ -795,7 +795,7 @@ public final class RedstoneWireTurbo { + upd.currentState = newState; + upd.type = UpdateNode.Type.REDSTONE; + upd.visited = true; +- nodeCache.put(pos, upd); ++ nodeCache.put(pos.immutable(), upd); // Leaf - Reduce optimiseRandomTick new BlockPos instance create - immutable + propagateChanges(worldIn, upd, 0); + + // Perform the walk over all directly reachable redstone wire blocks, propagating wire value +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index cf2a365bcbb1a278e5553c16ffef6d9ae81f4d41..592be2e7f28c9ee270d51aeea4615bbbe55e80f0 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1126,6 +1126,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(player -> player.stopSleepInBed(false, false)); + } + ++ public static final BlockPos.MutableBlockPos POS_CACHE = new BlockPos.MutableBlockPos(); // Leaf - Reduce optimiseRandomTick new BlockPos instance create ++ + // Paper start - optimise random ticking + private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) { + final LevelChunkSection[] sections = chunk.getSections(); +@@ -1160,14 +1162,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + final int location = tickList.getRaw(index); // Leaf - Micro optimizations for random tick - no unnecessary operations + final BlockState state = states.get(location); + +- // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! +- final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ); // Leaf - Micro optimizations for random tick - no redundant mask ++ // Leaf start - Reduce optimiseRandomTick new BlockPos instance create ++ final BlockPos pos = POS_CACHE.set((location & 15) | offsetX, (location >>> 8) | offsetY, ((location >>> 4) & 15) | offsetZ); ++ final BlockPos finalPos = org.dreeam.leaf.config.modules.opt.MutableBlockPos.enabled ? pos : pos.immutable(); ++ state.randomTick(this, finalPos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast ++ // Leaf end - Reduce optimiseRandomTick new BlockPos instance create + +- state.randomTick(this, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { +- fluidState.randomTick(this, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast ++ fluidState.randomTick(this, finalPos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast // Leaf - Reduce optimiseRandomTick new BlockPos instance create + } + } + } +diff --git a/net/minecraft/world/level/block/SculkSpreader.java b/net/minecraft/world/level/block/SculkSpreader.java +index 050f7595bda2e735ec112f70d6462042900b8c2c..3485a08070b1f021ee0dfb9b7456d0227599027b 100644 +--- a/net/minecraft/world/level/block/SculkSpreader.java ++++ b/net/minecraft/world/level/block/SculkSpreader.java +@@ -156,7 +156,7 @@ public class SculkSpreader { + if (chargeCursor.charge <= 0) { + level.levelEvent(LevelEvent.PARTICLES_SCULK_CHARGE, chargeCursor.getPos(), 0); + } else { +- BlockPos pos1 = chargeCursor.getPos(); ++ BlockPos pos1 = chargeCursor.getPos().immutable(); // Leaf - Reduce optimiseRandomTick new BlockPos instance create - immutable + map1.computeInt(pos1, (blockPos, integer) -> (integer == null ? 0 : integer) + chargeCursor.charge); + SculkSpreader.ChargeCursor chargeCursor1 = map.get(pos1); + if (chargeCursor1 == null) { +diff --git a/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java b/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java +index 453eb859b78fe6df909ef3bc478d6fdd848a96d1..a678bc008827ea128c484efd8126737bcfca93c3 100644 +--- a/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java ++++ b/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java +@@ -113,7 +113,7 @@ public class ExperimentalRedstoneWireEvaluator extends RedstoneWireEvaluator { + BlockState blockState = level.getBlockState(pos); + if (blockState.is(this.wireBlock)) { + this.setPower(pos, blockState.getValue(RedStoneWireBlock.POWER), orientation); +- this.wiresToTurnOff.add(pos); ++ this.wiresToTurnOff.add(pos.immutable()); // Leaf - Reduce optimiseRandomTick new BlockPos instance create - immutable + } else { + this.propagateChangeToNeighbors(level, pos, 0, orientation, true); + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/MutableBlockPos.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/MutableBlockPos.java new file mode 100644 index 0000000000..52adfee8b6 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/MutableBlockPos.java @@ -0,0 +1,29 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; + +public class MutableBlockPos extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + @Experimental + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".reuse-random-ticking-blockpos", enabled, + config.pickStringRegionBased( + """ + Experimental feature. + Reuse BlockPos to reduce memory allocation slightly and improve performance on random ticking. + May conflict with certain plugins or operations. Disable if position issues occur.""", + """ + 实验性功能 + 复用 BlockPos 以略微减少内存分配,提升 random ticking 的性能. + 可能与某些插件或操作冲突,如出现位置异常请关闭.""")); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java index 60768c22b6..31c374df56 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -53,10 +53,20 @@ public void tick(ServerLevel world) { } final long[] q = queue.elements(); final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(world) << 4; - for (int k = 0, len = queue.size(); k < len; ++k) { - final long packed = q[k]; - final LevelChunk chunk = raw[(int) (packed >>> SECTION_BITS)]; - tickBlock(world, chunk, (int) (packed & SECTION_MASK), random, minY); + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + if (org.dreeam.leaf.config.modules.opt.MutableBlockPos.enabled) { + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + for (int k = 0, len = queue.size(); k < len; ++k) { + final long packed = q[k]; + final LevelChunk chunk = raw[(int) (packed >>> SECTION_BITS)]; + tickBlockMutable(world, chunk, (int) (packed & SECTION_MASK), random, minY, doubleTickFluids, pos); + } + } else { + for (int k = 0, len = queue.size(); k < len; ++k) { + final long packed = q[k]; + final LevelChunk chunk = raw[(int) (packed >>> SECTION_BITS)]; + tickBlock(world, chunk, (int) (packed & SECTION_MASK), random, minY, doubleTickFluids); + } } } @@ -222,17 +232,32 @@ private static void iceSnow(ServerLevel world, int size, int randomTickSpeed, Bi } } - private static void tickBlock(ServerLevel world, LevelChunk chunk, int sectionIdx, BitRandomSource random, int minSection) { + private static void tickBlockMutable(ServerLevel world, LevelChunk chunk, int sectionIdx, BitRandomSource random, int minSection, boolean doubleTickFluids, BlockPos.MutableBlockPos pos) { LevelChunkSection section = chunk.getSection(sectionIdx); ShortList list = section.moonrise$getTickingBlockList(); int size = list.size(); if (size == 0) return; short location = list.getRaw(boundedNextInt(random, size)); BlockState state = section.states.get(location); - final BlockPos pos = new BlockPos((location & 15) | (chunk.locX << 4), (location >>> 8) | (minSection + (sectionIdx << 4)), ((location >>> 4) & 15) | (chunk.locZ << 4)); + pos.set((location & 15) | (chunk.locX << 4), (location >>> 8) | (minSection + (sectionIdx << 4)), ((location >>> 4) & 15) | (chunk.locZ << 4)); state.randomTick(world, pos, random); + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { + fluidState.randomTick(world, pos, random); + } + } + } - final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + private static void tickBlock(ServerLevel world, LevelChunk chunk, int sectionIdx, BitRandomSource random, int minSection, boolean doubleTickFluids) { + LevelChunkSection section = chunk.getSection(sectionIdx); + ShortList list = section.moonrise$getTickingBlockList(); + int size = list.size(); + if (size == 0) return; + short location = list.getRaw(boundedNextInt(random, size)); + BlockState state = section.states.get(location); + BlockPos pos = new BlockPos((location & 15) | (chunk.locX << 4), (location >>> 8) | (minSection + (sectionIdx << 4)), ((location >>> 4) & 15) | (chunk.locZ << 4)); + state.randomTick(world, pos, random); if (doubleTickFluids) { final FluidState fluidState = state.getFluidState(); if (fluidState.isRandomlyTicking()) {