9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00

fix random tick

This commit is contained in:
hayanesuru
2025-06-08 17:31:34 +09:00
parent 47c1783afc
commit aad17b0a5b
2 changed files with 28 additions and 207 deletions

View File

@@ -17,7 +17,7 @@ index 2f927b422c2c4f2f65d822befe3cbfd9e3bb3708..d0fcfeaf093b718c8acd6e057176d569
if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java 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 --- a/net/minecraft/server/level/ServerLevel.java
+++ b/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 @@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -33,13 +33,13 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..2efcdb9bc91b9106b4aef9e24cc20596
if (randomTickSpeed > 0) { if (randomTickSpeed > 0) {
- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking - 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 + 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 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 --- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/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 @@ -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 + // Leaf start - random tick
+ private long leaf$randomTickChance; + private long leaf$randomTickChance;
+ private long leaf$countTickingBlocks; + private int leaf$countTickingBlocks;
+ private long leaf$countTickingSections; + private int leaf$countTickingSections;
+ +
+ public final long leaf$randomTickChance() { + public final long leaf$randomTickChance() {
+ return leaf$randomTickChance; + return leaf$randomTickChance;
@@ -57,15 +57,15 @@ index 624a177695580510c0a49d4503dee72da7fd7114..affe9fff1ff2f7e221f8cfe345d40d70
+ public final void leaf$setRandomTickChance(long chance) { + public final void leaf$setRandomTickChance(long chance) {
+ leaf$randomTickChance = chance; + leaf$randomTickChance = chance;
+ } + }
+ public final long leaf$countTickingBlocks() { + public final int leaf$countTickingBlocks() {
+ return leaf$countTickingBlocks; + return leaf$countTickingBlocks;
+ } + }
+ public final long leaf$countTickingSections() { + public final int leaf$countTickingSections() {
+ return leaf$countTickingSections; + return leaf$countTickingSections;
+ } + }
+ public final void leaf$recompute() { + public final void leaf$recompute() {
+ long total1 = 0L; + int total1 = 0;
+ long total2 = 0L; + int total2 = 0;
+ for (LevelChunkSection section : sections) { + for (LevelChunkSection section : sections) {
+ total1 += section.moonrise$getTickingBlockList().size(); + total1 += section.moonrise$getTickingBlockList().size();
+ if (section.isRandomlyTickingBlocks()) { + if (section.isRandomlyTickingBlocks()) {

View File

@@ -16,15 +16,10 @@ import java.util.OptionalLong;
public final class RandomTickSystem { public final class RandomTickSystem {
private final LongArrayList tickPos = new LongArrayList(); private final LongArrayList tickPos = new LongArrayList();
private final WyRand rand = new WyRand(RandomSupport.generateUniqueSeed());
private static final long SCALE = 0x100000L; 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 = 4096L;
private static final long CHUNK_BLOCKS_HALF = 2048L; private static final int MASK = 0xfffff;
private static final int MASK_ONE_FOURTH = 0x300000;
private long cache = rand.next();
private int cacheIdx = 0;
public void tick(ServerLevel world) { public void tick(ServerLevel world) {
var simpleRandom = world.simpleRandom; var simpleRandom = world.simpleRandom;
@@ -43,9 +38,6 @@ public final class RandomTickSystem {
return; return;
} }
BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ());
if (state == null) {
return;
}
state.randomTick(world, pos, tickRand); state.randomTick(world, pos, tickRand);
if (doubleTickFluids) { if (doubleTickFluids) {
final FluidState fluidState = state.getFluidState(); final FluidState fluidState = state.getFluidState();
@@ -63,216 +55,45 @@ public final class RandomTickSystem {
chunk.leaf$setRandomTickChance(0L); chunk.leaf$setRandomTickChance(0L);
return 0L; return 0L;
} }
long product = tickSpeed * tickingCount; long chance = (tickSpeed * tickingCount * SCALE) / CHUNK_BLOCKS;
long chance = ((product + CHUNK_BLOCKS_HALF) * SCALE) / CHUNK_BLOCKS;
chunk.leaf$setRandomTickChance(chance); chunk.leaf$setRandomTickChance(chance);
return 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<net.minecraft.world.level.block.state.BlockState> 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( public void randomTickChunk(
RandomSource randomSource,
LevelChunk chunk, LevelChunk chunk,
long tickSpeed long tickSpeed
) { ) {
// reuse the random number one time int a = randomSource.nextInt();
long a; if ((a & MASK_ONE_FOURTH) != 0) {
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) {
return; return;
} }
// tick speed mul 4
tickSpeed = tickSpeed * 4; tickSpeed = tickSpeed * 4;
long chance = chunk.leaf$randomTickChance(); long chance = chunk.leaf$randomTickChance();
if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0L) {
// the chunk not exists random tickable block
if (chance == 0L && (chance = recompute(chunk, tickSpeed)) == 0) {
return; return;
} }
if (chance >= (long) (a & MASK) || (chance = recompute(chunk, tickSpeed)) == 0L) {
// 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)) {
return; return;
} }
int tickingCount = chunk.leaf$countTickingBlocks();
// recompute tickable block and chance. OptionalLong pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount));
//
// 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
if (pos.isPresent()) { if (pos.isPresent()) {
tickPos.add(pos.getAsLong()); tickPos.add(pos.getAsLong());
} }
// chance less than one if (chance > SCALE) {
// 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());
}
chance -= SCALE; chance -= SCALE;
} long last = randomSource.nextInt() & MASK;
} while (last < chance) {
pos = chunk.leaf$tickingPos(randomSource.nextInt(tickingCount));
private final static class WyRand implements BitRandomSource { if (pos.isPresent()) {
private long state; tickPos.add(pos.getAsLong());
}
private static final long WY0 = 0x2d35_8dcc_aa6c_78a5L; chance -= SCALE;
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);
} }
} }
} }