diff --git a/leaf-server/minecraft-patches/features/0191-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0191-optimize-random-tick.patch index d91c69c1..fbfca7b3 100644 --- a/leaf-server/minecraft-patches/features/0191-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0191-optimize-random-tick.patch @@ -17,7 +17,7 @@ index 15bbd1f7f2a90b4b5427026d622764bb1c92d465..0682e8b1708ed39e97b53548301c353c if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb849c57992658005e0f514c6f7923f8ca43bebf..0bf765334f20fa5a999400076797d5b1f82c7469 100644 +index eb849c57992658005e0f514c6f7923f8ca43bebf..9f0313038693a1e571beba0c8ca027c29789a1ce 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -33,7 +33,7 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..0bf765334f20fa5a999400076797d5b1 if (randomTickSpeed > 0) { - this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking -+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick ++ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.tickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick + else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick } } 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 f917ad80..69c7aea7 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 @@ -14,7 +14,13 @@ public final class RandomTickSystem { private static final long SCALE = 0x100000L; private static final long CHUNK_BLOCKS = 4096L; - private final LongArrayList queue = new LongArrayList(); + /// reduce unnecessary sampling and block counting + private static final long TICK_MASK = 0b11L; + private static final long TICK_MUL = 4L; + private static final int BITS_STEP = 2; + private static final int BITS_MAX = 60; + + private final LongArrayList edges = new LongArrayList(); private final LongArrayList samples = new LongArrayList(); private final LongArrayList weights = new LongArrayList(); private long weightsSum = 0L; @@ -39,91 +45,117 @@ public final class RandomTickSystem { final long[] weightsRaw = weights.elements(); final long[] samplesRaw = samples.elements(); final long spoke = weightsSum / chosen; + if (spoke == 0L) { + return; + } long accumulated = weightsRaw[0]; long current = boundedNextLong(random, spoke); int i = 0; - while (current < weightsSum) { + + long packed1 = 0L, packed2 = 0L, packed3 = 0L, packed4; + int rest; + long count = 0L; + while (true) { + if (current >= weightsSum) { + rest = 0; + break; + } while (accumulated < current) { i += 1; accumulated += weightsRaw[i]; } - queue.add(samplesRaw[i]); + packed1 = samplesRaw[i]; current += spoke; - } - while (queue.size() < chosen) { - queue.push(samplesRaw[i]); - } - weightsSum = 0L; - weights.clear(); - samples.clear(); - var simpleRandom = world.simpleRandom; - int j = queue.size(); - long[] queueRaw = queue.elements(); - int k = j - 3; - // optimize getChunk for large tickable block chunk or high tick speed - for (i = 0; i < k; i += 4) { - long packed1 = queueRaw[i]; - long packed2 = queueRaw[i + 1]; - long packed3 = queueRaw[i + 2]; - long packed4 = queueRaw[i + 3]; - int x; - int z; - LevelChunk chunk1; - LevelChunk chunk2; - LevelChunk chunk3; - LevelChunk chunk4; + if (current >= weightsSum) { + rest = 1; + break; + } + while (accumulated < current) { + i += 1; + accumulated += weightsRaw[i]; + } + packed2 = samplesRaw[i]; + current += spoke; - x = (int) packed1; - z = (int) (packed1 >> 32); - chunk1 = world.chunkSource.getChunkAtIfLoadedImmediately(x, z); + if (current >= weightsSum) { + rest = 2; + break; + } + while (accumulated < current) { + i += 1; + accumulated += weightsRaw[i]; + } + packed3 = samplesRaw[i]; + current += spoke; - if (packed1 != packed2) { - x = (int) packed2; - z = (int) (packed2 >> 32); - chunk2 = world.chunkSource.getChunkAtIfLoadedImmediately(x, z); - } else { - chunk2 = chunk1; + if (current >= weightsSum) { + rest = 3; + break; } - if (packed2 != packed3) { - x = (int) packed3; - z = (int) (packed3 >> 32); - chunk3 = world.chunkSource.getChunkAtIfLoadedImmediately(x, z); - } else { - chunk3 = chunk2; - } - if (packed3 != packed4) { - x = (int) packed4; - z = (int) (packed4 >> 32); - chunk4 = world.chunkSource.getChunkAtIfLoadedImmediately(x, z); - } else { - chunk4 = chunk3; + while (accumulated < current) { + i += 1; + accumulated += weightsRaw[i]; } + packed4 = samplesRaw[i]; + current += spoke; + + final LevelChunk chunk1; + final LevelChunk chunk2; + final LevelChunk chunk3; + final LevelChunk chunk4; + chunk1 = getChunk(world, packed1); + chunk2 = packed1 != packed2 ? getChunk(world, packed2) : chunk1; + chunk3 = packed2 != packed3 ? getChunk(world, packed3) : chunk2; + chunk4 = packed3 != packed4 ? getChunk(world, packed4) : chunk3; if (chunk1 != null) { - tickBlock(world, chunk1, simpleRandom); + tickBlock(world, chunk1, random); } if (chunk2 != null) { - tickBlock(world, chunk2, simpleRandom); + tickBlock(world, chunk2, random); } if (chunk3 != null) { - tickBlock(world, chunk3, simpleRandom); + tickBlock(world, chunk3, random); } if (chunk4 != null) { - tickBlock(world, chunk4, simpleRandom); + tickBlock(world, chunk4, random); } + + count++; } - for (; i < j; i++) { - long packed = queueRaw[i]; - int x = (int) packed; - int z = (int) (packed >> 32); - LevelChunk chunk = world.chunkSource.getChunkAtIfLoadedImmediately(x, z); + + if (rest >= 1) { + edges.push(packed1); + } + if (rest >= 2) { + edges.push(packed2); + } + if (rest == 3) { + edges.push(packed3); + } + chosen -= rest; + chosen -= count * 4L; + while (edges.size() < chosen) { + edges.push(samplesRaw[i]); + } + + long[] queueRaw = edges.elements(); + for (int k = 0, j = edges.size(); k < j; k++) { + LevelChunk chunk = getChunk(world, queueRaw[k]); if (chunk != null) { - tickBlock(world, chunk, simpleRandom); + tickBlock(world, chunk, random); } } - queue.clear(); + weightsSum = 0L; + edges.clear(); + weights.clear(); + samples.clear(); + } + + private static LevelChunk getChunk(ServerLevel world, long packed) { + return world.chunkSource.getChunkAtIfLoadedImmediately((int) packed, (int) (packed >> 32)); } private static void tickBlock(ServerLevel world, LevelChunk chunk, RandomSource random) { @@ -144,25 +176,34 @@ public final class RandomTickSystem { } } - public void randomTickChunk( + public void tickChunk( RandomSource random, LevelChunk chunk, long tickSpeed ) { - if (this.bits == 60) { + if (this.bits == BITS_MAX) { this.bits = 0; this.cacheRandom = random.nextLong(); } else { - this.bits += 2; + this.bits += BITS_STEP; } - if ((this.cacheRandom & (0b11L << bits)) == 0L && chunk.leaf$tickingBlocksCount() != 0L) { - long chance = (4 * tickSpeed * chunk.leaf$lastTickingBlocksCount() * SCALE) / CHUNK_BLOCKS; + if ((this.cacheRandom & (TICK_MASK << bits)) == 0L && chunk.leaf$tickingBlocksCount() != 0L) { + long chance = (TICK_MUL * tickSpeed * chunk.leaf$lastTickingBlocksCount() * SCALE) / CHUNK_BLOCKS; samples.add(chunk.getPos().longKey); weights.add(chance); weightsSum += chance; } } + /** + * @param rng a random number generator to be used as a + * source of pseudorandom {@code long} values + * @param bound the upper bound (exclusive); must be greater than zero + * + * @return a pseudorandomly chosen {@code long} value + * + * @see java.util.random.RandomGenerator#nextLong(long) nextLong(bound) + */ public static long boundedNextLong(RandomSource rng, long bound) { final long m = bound - 1; long r = rng.nextLong();