diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch index 0eafd9e4..8794f7f0 100644 --- a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch @@ -17,7 +17,7 @@ index 2f927b422c2c4f2f65d822befe3cbfd9e3bb3708..d0fcfeaf093b718c8acd6e057176d569 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..2efcdb9bc91b9106b4aef9e24cc20596be4a5661 100644 +index eb849c57992658005e0f514c6f7923f8ca43bebf..0bf765334f20fa5a999400076797d5b1f82c7469 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,13 +33,13 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596 if (randomTickSpeed > 0) { - this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking -+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(chunk, randomTickSpeed); // Leaf - random tick ++ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.randomTickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick + else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick } } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d707e0f3dbc 100644 +index 624a177695580510c0a49d4503dee72da7fd7114..f85d46c23824de177fe0c08b2ce6fbbb81c3535b 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -151,6 +151,52 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -48,8 +48,8 @@ index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d70 + // Leaf start - random tick + private long leaf$randomTickChance; -+ private long leaf$countTickingBlocks; -+ private long leaf$countTickingSections; ++ private int leaf$countTickingBlocks; ++ private int leaf$countTickingSections; + + public final long leaf$randomTickChance() { + return leaf$randomTickChance; @@ -57,15 +57,15 @@ index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d70 + public final void leaf$setRandomTickChance(long chance) { + leaf$randomTickChance = chance; + } -+ public final long leaf$countTickingBlocks() { ++ public final int leaf$countTickingBlocks() { + return leaf$countTickingBlocks; + } -+ public final long leaf$countTickingSections() { ++ public final int leaf$countTickingSections() { + return leaf$countTickingSections; + } + public final void leaf$recompute() { -+ long total1 = 0L; -+ long total2 = 0L; ++ int total1 = 0; ++ int total2 = 0; + for (LevelChunkSection section : sections) { + total1 += section.moonrise$getTickingBlockList().size(); + if (section.isRandomlyTickingBlocks()) { 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 ba234e07..afff1346 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 @@ -16,15 +16,10 @@ import java.util.OptionalLong; public final class RandomTickSystem { private final LongArrayList tickPos = new LongArrayList(); - private final WyRand rand = new WyRand(RandomSupport.generateUniqueSeed()); private static final long SCALE = 0x100000L; - private static final long MASK = 0xfffffL; - private static final long MASK_ONE_FOURTH = 0x300000L; private static final long CHUNK_BLOCKS = 4096L; - private static final long CHUNK_BLOCKS_HALF = 2048L; - - private long cache = rand.next(); - private int cacheIdx = 0; + private static final int MASK = 0xfffff; + private static final int MASK_ONE_FOURTH = 0x300000; public void tick(ServerLevel world) { var simpleRandom = world.simpleRandom; @@ -43,9 +38,6 @@ public final class RandomTickSystem { return; } BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); - if (state == null) { - return; - } state.randomTick(world, pos, tickRand); if (doubleTickFluids) { final FluidState fluidState = state.getFluidState(); @@ -63,216 +55,45 @@ public final class RandomTickSystem { chunk.leaf$setRandomTickChance(0L); return 0L; } - long product = tickSpeed * tickingCount; - long chance = ((product + CHUNK_BLOCKS_HALF) * SCALE) / CHUNK_BLOCKS; + long chance = (tickSpeed * tickingCount * SCALE) / CHUNK_BLOCKS; chunk.leaf$setRandomTickChance(chance); return chance; } -/* - public void randomTickChunkOrigin( - ServerLevel level, - LevelChunk chunk, - long tickSpeed - ) { - final LevelChunkSection[] sections = chunk.getSections(); - final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level); // Leaf - Micro optimizations for random tick - no redundant cast - final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = level.simpleRandom; // Leaf - Faster random generator - upcasting - final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); - - final ChunkPos cpos = chunk.getPos(); - final int offsetX = cpos.x << 4; - final int offsetZ = cpos.z << 4; - - for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { - // Leaf start - Micro optimizations for random tick - final LevelChunkSection section = sections[sectionIndex]; - if (!section.isRandomlyTickingBlocks()) { - continue; - } - final int offsetY = (sectionIndex + minSection) << 4; - final net.minecraft.world.level.chunk.PalettedContainer states = section.states; - // Leaf end - Micro optimizations for random tick - - final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); // Leaf - Micro optimizations for random tick - no redundant cast - - for (int i = 0; i < tickSpeed; ++i) { - final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); - - if (index >= tickList.size()) { // Leaf - Micro optimizations for random tick - inline one-time value - // most of the time we fall here - continue; - } - - 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 - - state.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast - if (doubleTickFluids) { - final FluidState fluidState = state.getFluidState(); - if (fluidState.isRandomlyTicking()) { - fluidState.randomTick(level, pos, simpleRandom); // Leaf - Micro optimizations for random tick - no redundant cast - } - } - } - } - } -*/ public void randomTickChunk( + RandomSource randomSource, LevelChunk chunk, long tickSpeed ) { - // reuse the random number one time - long a; - if (cacheIdx == 0) { - a = cache; - cacheIdx = 1; - } else if (cacheIdx == 1) { - a = cache >>> 32; - cacheIdx = 2; - } else { - a = cache = rand.next(); - cacheIdx = 0; - } - - // 25% chance tick - if ((a & MASK_ONE_FOURTH) != 0L) { + int a = randomSource.nextInt(); + if ((a & MASK_ONE_FOURTH) != 0) { return; } - // tick speed mul 4 tickSpeed = tickSpeed * 4; long chance = chunk.leaf$randomTickChance(); - - // the chunk not exists random tickable block - if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0) { + if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0L) { return; } - - // this is correct, don't modify - // - // when chance eq 0.1 - // - skip: 0.1 >= 0.1..1.0 - // - tick: 0.1 < 0.0..0.1 - // - // (chance not newest) - if (chance >= (a & MASK)) { + if (chance >= (long) (a & MASK) || (chance = recompute(chunk, tickSpeed)) == 0L) { return; } - - // recompute tickable block and chance. - // - // chance for next randomTickChunk - // tickingCount used this tick - // - // chance != 0 - // - // always tick block when chance > 1 - if ((chance = recompute(chunk, tickSpeed)) == 0) { - return; - } - long tickingCount = chunk.leaf$countTickingBlocks(); - - // tick one block base on chance - // fairly pick a random tickable block - // vanilla may do more tick - // recompute computed chance for that part - // so the tick count should same as vanilla - int randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); - OptionalLong pos = chunk.leaf$tickingPos(randPos); - // always true + int tickingCount = chunk.leaf$countTickingBlocks(); + OptionalLong pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); if (pos.isPresent()) { tickPos.add(pos.getAsLong()); } - // chance less than one - // most case - if (chance < SCALE) { - return; - } - - // attempt to do more tick - // - // chance == 1.5 - // - loop 1: 50% tick - // - loop 2: never - // - // chance == 2.5 - // - loop 1: always - // - loop 2: 50% - // - loop 3: never - - chance -= SCALE; - long last = rand.next() & MASK; - while (last < chance) { - randPos = (int) ((rand.next() & Integer.MAX_VALUE) % tickingCount); - pos = chunk.leaf$tickingPos(randPos); - // always true - if (pos.isPresent()) { - tickPos.add(pos.getAsLong()); - } + if (chance > SCALE) { chance -= SCALE; - } - } - - private final static class WyRand implements BitRandomSource { - private long state; - - private static final long WY0 = 0x2d35_8dcc_aa6c_78a5L; - private static final long WY1 = 0x8bb8_4b93_962e_acc9L; - private static final int BITS = 64; - - public WyRand(long seed) { - this.state = seed; - } - - @Override - public int next(int bits) { - return (int)(this.next() >>> (BITS - bits)); - } - - @Override - public @NotNull RandomSource fork() { - return new WyRand(next()); - } - - @Override - public @NotNull PositionalRandomFactory forkPositional() { - throw new UnsupportedOperationException("forkPositional"); - } - - @Override - public void setSeed(long seed) { - this.state = seed; - } - - public int nextInt() { - return (int) (next() & Integer.MAX_VALUE); - } - - @Override - public double nextGaussian() { - throw new UnsupportedOperationException("nextGaussian"); - } - - public long next() { - long seed = this.state; - seed += WY0; - long aLow = seed & 0xFFFFFFFFL; - long aHigh = seed >>> 32; - long bLow = (seed ^ WY1) & 0xFFFFFFFFL; - long bHigh = (seed ^ WY1) >>> 32; - long loLo = aLow * bLow; - long hiLo = aHigh * bLow; - long loHi = aLow * bHigh; - long hiHi = aHigh * bHigh; - long mid1 = (loLo >>> 32) + (hiLo & 0xFFFFFFFFL) + (loHi & 0xFFFFFFFFL); - long mid2 = (hiLo >>> 32) + (loHi >>> 32) + (mid1 >>> 32); - this.state = seed; - return ((loLo & 0xFFFFFFFFL) | (mid1 << 32)) ^ hiHi + (mid2 & 0xFFFFFFFFL); + long last = randomSource.nextInt() & MASK; + while (last < chance) { + pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount)); + if (pos.isPresent()) { + tickPos.add(pos.getAsLong()); + } + chance -= SCALE; + } } } }