9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-27 10:59:16 +00:00

Update changes from ver/1.21.4 branch

This commit is contained in:
Dreeam
2025-06-22 10:59:09 +08:00
22 changed files with 905 additions and 524 deletions

View File

@@ -7,48 +7,78 @@ Original license: MIT
Original project: https://github.com/thebrightspark/AsyncLocator
diff --git a/net/minecraft/server/commands/LocateCommand.java b/net/minecraft/server/commands/LocateCommand.java
index a734b2597c3491db35d9660e169f8e8b6320900b..8a79a339757d6ac49713bf6db0fb675d9893fd1b 100644
index a734b2597c3491db35d9660e169f8e8b6320900b..5274f09b0abf148aea1c0baa39edbfdac1acc4f5 100644
--- a/net/minecraft/server/commands/LocateCommand.java
+++ b/net/minecraft/server/commands/LocateCommand.java
@@ -106,6 +106,38 @@ public class LocateCommand {
@@ -106,6 +106,34 @@ public class LocateCommand {
BlockPos blockPos = BlockPos.containing(source.getPosition());
ServerLevel level = source.getLevel();
Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER);
+ // Leaf start - Asynchronous locator
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) {
+ net.minecraft.commands.CommandSource locatorSource = source.source;
+ if (locatorSource instanceof net.minecraft.server.level.ServerPlayer || locatorSource instanceof net.minecraft.server.MinecraftServer) {
+ BlockPos originPos = BlockPos.containing(source.getPosition());
+ BlockPos originPos = BlockPos.containing(source.getPosition());
+ org.dreeam.leaf.async.locate.AsyncLocator.locate(source.getLevel(), holderSet, originPos, 100, false)
+ .thenOnServerThread(pair -> {
+ stopwatch.stop();
+ if (pair != null) {
+ showLocateResult(
+ source,
+ structure,
+ originPos,
+ pair,
+ "commands.locate.structure.success",
+ false,
+ stopwatch.elapsed()
+ );
+ } else {
+ source.sendFailure(
+ Component.literal(
+ ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable()).getMessage()
+ )
+ );
+ }
+ });
+
+ org.dreeam.leaf.async.locate.AsyncLocator.locate(source.getLevel(), holderSet, originPos, 100, false)
+ .thenOnServerThread(pair -> {
+ stopwatch.stop();
+ if (pair != null) {
+ showLocateResult(
+ source,
+ structure,
+ originPos,
+ pair,
+ "commands.locate.structure.success",
+ false,
+ stopwatch.elapsed()
+ );
+ } else {
+ source.sendFailure(
+ Component.literal(
+ ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable()).getMessage()
+ )
+ );
+ }
+ });
+
+ return 0;
+ }
+ return 0;
+ }
+ // Leaf end - Asynchronous locator
Pair<BlockPos, Holder<Structure>> pair = level.getChunkSource().getGenerator().findNearestMapStructure(level, holderSet, blockPos, 100, false);
stopwatch.stop();
if (pair == null) {
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index eaaa66c4d86d4ebda0acf8f1dbe8ecb55aa28285..8f41326fda8c5f9f6926038508be6c6529b051bc 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -852,14 +852,25 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@Override
public boolean pollTask() {
+ // Leaf start - Async Locator
// Paper start - rewrite chunk system
- final ServerChunkCache serverChunkCache = ServerChunkCache.this;
- if (serverChunkCache.runDistanceManagerUpdates()) {
- return true;
+ java.util.function.Supplier<Boolean> supplier = () -> {
+ final ServerChunkCache serverChunkCache = ServerChunkCache.this;
+ if (serverChunkCache.runDistanceManagerUpdates()) {
+ return true;
+ } else {
+ return super.pollTask() | ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) serverChunkCache.level).moonrise$getChunkTaskScheduler().executeMainThreadTask();
+ }
+ };
+ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && Thread.currentThread() instanceof org.dreeam.leaf.async.locate.AsyncLocator.AsyncLocatorThread) {
+ return MinecraftServer.getServer().scheduleWithResult((java.util.concurrent.CompletableFuture<Boolean> future) -> {
+ future.complete(supplier.get());
+ }).join();
} else {
- return super.pollTask() | ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)serverChunkCache.level).moonrise$getChunkTaskScheduler().executeMainThreadTask();
+ return supplier.get();
}
// Paper end - rewrite chunk system
+ // Leaf end - Async Locator
}
}
}
diff --git a/net/minecraft/world/entity/animal/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java
index 23696a5e2871ea07f34d4b4f6a20e2896ac3f5bd..c4fda92e078c9ba745b2548ecaaffffff97fb0fd 100644
--- a/net/minecraft/world/entity/animal/Dolphin.java

View File

@@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leymooo <Vjatseslav.Maspanov@gmail.com>
Date: Wed, 11 Jun 2025 05:30:39 +0300
Subject: [PATCH] PaperPR: Fix excess slot updates / inventory state id desync
Original license: GPLv3
Original project: https://github.com/SparklyPower/SparklyPaper
Paper pull request: https://github.com/PaperMC/Paper/pull/12654
Fixes inventory state id desync with high ping causing inventory to be "buggy" (ghost items, items jumping to/from cursor, etc)
Without patch: https://youtu.be/hOXt3Rpkgtg
With patch: https://youtu.be/MtOYD7uieS4
https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/df01cf867a2255024aacf9ac2ff6a56b2ffb7ce5#nms-patches/net/minecraft/world/inventory/Container.patch
https://discord.com/channels/289587909051416579/555462289851940864/1382165293308182699
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index 9dd3187fd968ab95e9d55b4c8cc74e782cc0f241..c5ede24d00f444d04c835af3a7f0154227492be4 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -559,7 +559,7 @@ public abstract class AbstractContainerMenu {
slot.setChanged();
// CraftBukkit start - Make sure the client has the right slot contents
- if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != 64) {
+ if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != net.minecraft.world.Container.MAX_STACK) { // Paper - Fix excess slot updates / inventory state id desync
serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), slot.index, slot.getItem()));
// Updating a crafting inventory makes the client reset the result slot, have to send it again
if (this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.WORKBENCH || this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.CRAFTING) {

View File

@@ -0,0 +1,105 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <hayanesuru@outlook.jp>
Date: Fri, 6 Jun 2025 20:46:10 +0900
Subject: [PATCH] optimize random tick
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 46e171ca454253c32e22c0c18587e9a7ba19f331..36e592ff42eba050829f9c4d055c3d49a78ba813 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -633,6 +633,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
} finally {
list.clear();
}
+ this.level.randomTickSystem.tick(this.level); // Leaf - optimize random tick
this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimisations
if (_boolean) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 27da552e2542153a58d6177f592cf30d858c41a9..a180612fea46ec9f7dcfe1781e985dd2e98ed513 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1114,6 +1114,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking
+ public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting
ChunkPos pos = chunk.getPos();
@@ -1129,7 +1130,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
} // Paper - Option to disable ice and snow
if (randomTickSpeed > 0) {
- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking
+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.tickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - optimize random tick
+ else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - optimize random tick
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index a3674ddb883eecb255279375a5e2eece7e016c0f..634bf0c15eb63aeb5ff06dd9b43ecbab627b0128 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -152,6 +152,48 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
}
// Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
+ // Leaf start - optimize random tick
+ private boolean leaf$tickingBlocksDirty = true;
+ private int leaf$tickingBlocksCount;
+ private int leaf$firstTickingSectionIndex = -1;
+ public final int leaf$tickingBlocksCount() {
+ if (!leaf$tickingBlocksDirty) {
+ return leaf$tickingBlocksCount;
+ }
+ leaf$tickingBlocksDirty = false;
+ int sum = 0;
+ leaf$firstTickingSectionIndex = -1;
+ for (int i = 0; i < sections.length; i++) {
+ LevelChunkSection section = sections[i];
+ int size = section.moonrise$getTickingBlockList().size();
+ if (size != 0 && leaf$firstTickingSectionIndex == -1) {
+ leaf$firstTickingSectionIndex = i;
+ }
+ sum += size;
+ }
+ leaf$tickingBlocksCount = sum;
+ return sum;
+ }
+ public final java.util.OptionalLong leaf$getTickingPos(int idx) {
+ if (leaf$firstTickingSectionIndex != -1) {
+ for (int i = leaf$firstTickingSectionIndex; i < sections.length; i++) {
+ LevelChunkSection section = sections[i];
+ var l = section.moonrise$getTickingBlockList();
+ int size = l.size();
+ if (idx < size) {
+ short loc = l.getRaw(idx);
+ int x = (loc & 15) | (chunkPos.x << 4);
+ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4);
+ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4);
+ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z));
+ }
+ idx -= size;
+ }
+ }
+ leaf$tickingBlocksDirty = true;
+ return java.util.OptionalLong.empty();
+ }
+ // Leaf end - optimize random tick
public LevelChunk(Level level, ChunkPos pos) {
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
}
@@ -416,6 +458,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
if (blockState == state) {
return null;
} else {
+ // Leaf start - optimize random tick
+ if (blockState.isRandomlyTicking() != state.isRandomlyTicking()) {
+ leaf$tickingBlocksDirty = true;
+ }
+ // Leaf end - optimize random tick
Block block = state.getBlock();
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state);
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state);

View File

@@ -100,7 +100,7 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194
- // Paper end - detailed watchdog information
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 12cf6dc9bac109a4feeeeede5f3762b20ea582bb..b450ac5e19d42765c739311de1566b7b0c11c88b 100644
index 36e592ff42eba050829f9c4d055c3d49a78ba813..b32cb1c85a9f7a7a96a257c4546ee7e21cd91a6d 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -623,8 +623,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -117,10 +117,10 @@ index 12cf6dc9bac109a4feeeeede5f3762b20ea582bb..b450ac5e19d42765c739311de1566b7b
for (LevelChunk levelChunk : list) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 117a8ef98c369bf0919e44fe823d6af0758816b5..47357e1d54563e30b4281c410a1ab70ccd672ad0 100644
index a180612fea46ec9f7dcfe1781e985dd2e98ed513..7cb8329fc787aa6a9f877afb43dc9cd655cb0e90 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1526,13 +1526,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1518,13 +1518,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper end - log detailed entity tick information
public void tickNonPassenger(Entity entity) {
@@ -134,7 +134,7 @@ index 117a8ef98c369bf0919e44fe823d6af0758816b5..47357e1d54563e30b4281c410a1ab70c
entity.setOldPosAndRot();
entity.tickCount++;
entity.totalEntityAge++; // Paper - age-like counter for all entities
@@ -1545,13 +1539,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1537,13 +1531,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
for (Entity entity1 : entity.getPassengers()) {
this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}
@@ -149,7 +149,7 @@ index 117a8ef98c369bf0919e44fe823d6af0758816b5..47357e1d54563e30b4281c410a1ab70c
private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 7573c566aace4b7661c5aed6d96653472bab81bb..84808bf3311067409aad99817d3f1df2c098d83c 100644
index fc39af434b59054260d763b994fb50bcf92b4ff3..ddc2a7fbca414b8e3c044cfe7076d2cac182b63e 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1166,16 +1166,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess

View File

@@ -1,6 +1,5 @@
package org.dreeam.leaf.async.ai;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
@@ -10,85 +9,54 @@ import org.dreeam.leaf.config.modules.async.AsyncTargetFinding;
import org.dreeam.leaf.util.queue.SpscIntQueue;
import java.util.OptionalInt;
import java.util.concurrent.locks.LockSupport;
public class AsyncGoalExecutor {
protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal");
protected final SpscIntQueue queue;
protected final SpscIntQueue wake;
protected final IntArrayList submit;
private final ServerLevel world;
private long midTickCount = 0L;
public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) {
public AsyncGoalExecutor(ServerLevel world) {
this.world = world;
this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.submit = new IntArrayList();
}
boolean wake(int id) {
Entity entity = this.world.getEntities().get(id);
if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) {
return false;
}
mob.goalSelector.ctx.wake();
mob.targetSelector.ctx.wake();
return true;
}
public final void submit(int entityId) {
this.submit.add(entityId);
}
public final void tick() {
batchSubmit();
boolean wakeAll() {
boolean success = false;
while (true) {
OptionalInt result = this.wake.recv();
OptionalInt result = queue.recv();
if (result.isEmpty()) {
break;
}
int id = result.getAsInt();
if (poll(id) && !this.queue.send(id)) {
do {
wake(id);
} while (poll(id));
}
success = true;
wake(id);
}
return success;
}
private void batchSubmit() {
if (submit.isEmpty()) {
public void tickMob(Mob mob) {
if (!poll(mob)) {
return;
}
int[] raw = submit.elements();
int size = submit.size();
for (int i = 0; i < size; i++) {
int id = raw[i];
if (poll(id) && !this.queue.send(id)) {
do {
wake(id);
} while (poll(id));
}
int entityId = mob.getId();
if (!this.queue.send(entityId)) {
do {
wake(entityId);
} while (poll(mob));
}
this.submit.clear();
}
public final void midTick() {
if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) {
batchSubmit();
}
midTickCount += 1;
}
private boolean poll(int id) {
private void wake(int id) {
Entity entity = this.world.getEntities().get(id);
if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) {
return false;
return;
}
mob.goalSelector.ctx.wake();
mob.targetSelector.ctx.wake();
}
private boolean poll(Mob mob) {
try {
mob.tickingTarget = true;
boolean a = mob.targetSelector.poll();
@@ -97,8 +65,7 @@ public class AsyncGoalExecutor {
return a || b;
} catch (Exception e) {
LOGGER.error("Exception while polling", e);
// retry
return true;
return false;
}
}
}

View File

@@ -22,26 +22,11 @@ public class AsyncGoalThread extends Thread {
while (RUNNING) {
boolean retry = false;
for (ServerLevel level : server.getAllLevels()) {
var exec = level.asyncGoalExecutor;
while (true) {
OptionalInt result = exec.queue.recv();
if (result.isEmpty()) {
break;
}
int id = result.getAsInt();
retry = true;
if (exec.wake(id)) {
while (!exec.wake.send(id)) {
Thread.onSpinWait();
}
}
}
Thread.yield();
retry |= level.asyncGoalExecutor.wakeAll();
}
if (!retry) {
LockSupport.parkNanos(10_000L);
LockSupport.parkNanos(1_000_000L);
}
}
}

View File

@@ -7,6 +7,7 @@ import net.minecraft.Util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dreeam.leaf.config.modules.misc.SentryDSN;
import org.dreeam.leaf.config.modules.opt.FastBiomeManagerSeedObfuscation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.bukkit.Bukkit;
@@ -243,6 +244,7 @@ public class LeafConfig {
List<String> extraHidden = existing != null ? new ArrayList<>(Arrays.asList(existing.split(","))) : new ArrayList<>();
extraHidden.add(SentryDSN.sentryDsnConfigPath); // Hide Sentry DSN key
extraHidden.add(FastBiomeManagerSeedObfuscation.seedObfKeyPath); // Hide FastBiomeManagerSeedObfuscation key
return extraHidden;
}

View File

@@ -16,7 +16,6 @@ public class AsyncTargetFinding extends ConfigModules {
public static boolean searchBlock = true;
public static boolean searchEntity = true;
public static int queueSize = 4096;
public static long threshold = 10L;
private static boolean asyncTargetFindingInitialized;
@Override
@@ -36,21 +35,17 @@ public class AsyncTargetFinding extends ConfigModules {
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
// Disable if parallel world ticking is enabled, as they are incompatible.
if (enabled && SparklyPaperParallelWorldTicking.enabled) {
LeafConfig.LOGGER.warn("Async Target Finding is incompatible with Parallel World Ticking. Disabling Async Target Finding automatically.");
LeafConfig.LOGGER.warn("Async target finding is incompatible with Parallel World Ticking. Disabling Async target finding automatically.");
enabled = false;
}
alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true);
searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true);
searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true);
queueSize = config.getInt(getBasePath() + ".queue-size", 0);
threshold = config.getLong(getBasePath() + ".threshold", 0);
if (queueSize <= 0) {
queueSize = 4096;
}
if (threshold == 0L) {
threshold = 10L;
}
if (!enabled) {
alertOther = false;
searchEntity = false;

View File

@@ -15,6 +15,7 @@ public class FastBiomeManagerSeedObfuscation extends ConfigModules {
@Experimental
public static boolean enabled = false;
public static long seedObfuscationKey = ThreadLocalRandom.current().nextLong();
public static String seedObfKeyPath;
@Override
public void onLoaded() {
@@ -26,7 +27,7 @@ public class FastBiomeManagerSeedObfuscation extends ConfigModules {
"""
**实验性功能**
将原版 BiomeManager 的 SHA-256 种子混淆换成 XXHash."""));
seedObfuscationKey = config.getLong(getBasePath() + ".seed-obfuscation-key", seedObfuscationKey,
seedObfuscationKey = config.getLong(seedObfKeyPath = getBasePath() + ".seed-obfuscation-key", seedObfuscationKey,
config.pickStringRegionBased(
"Seed obfuscation key for XXHash.",
"XXHash 的混淆种子."));

View File

@@ -0,0 +1,20 @@
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 OptimizeRandomTick extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.PERF.getBaseKeyName() + ".optimise-random-tick";
}
@Experimental
public static boolean enabled = false;
@Override
public void onLoaded() {
enabled = config.getBoolean(getBasePath(), enabled);
}
}

View File

@@ -56,11 +56,7 @@ public class FasterRandomSource implements BitRandomSource {
@Override
public final int next(int bits) {
if (useDirectImpl) {
return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits));
}
return (int) ((seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits));
return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits));
}
public static class FasterRandomSourcePositionalRandomFactory implements PositionalRandomFactory {

View File

@@ -0,0 +1,168 @@
package org.dreeam.leaf.world;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.material.FluidState;
import java.util.OptionalLong;
public final class RandomTickSystem {
private static final long SCALE = 0x100000L;
private static final long CHUNK_BLOCKS = 4096L;
/// 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 queue = new LongArrayList();
private final LongArrayList samples = new LongArrayList();
private final LongArrayList weights = new LongArrayList();
private long weightsSum = 0L;
private int bits = 60;
private long cacheRandom = 0L;
public void tick(ServerLevel world) {
if (weights.isEmpty() || samples.isEmpty()) {
return;
}
final var random = world.simpleRandom;
final long chosen;
if (((weightsSum % SCALE) >= boundedNextLong(random, SCALE))) {
chosen = weightsSum / SCALE + 1L;
} else {
chosen = weightsSum / SCALE;
}
if (chosen == 0L) {
return;
}
final long spoke = weightsSum / chosen;
if (spoke == 0L) {
return;
}
final long[] weightsRaw = weights.elements();
final long[] samplesRaw = samples.elements();
long accumulated = weightsRaw[0];
long current = boundedNextLong(random, spoke);
int i = 0;
while (current < weightsSum) {
while (accumulated < current) {
i += 1;
accumulated += weightsRaw[i];
}
queue.add(samplesRaw[i]);
current += spoke;
}
while (queue.size() < chosen) {
queue.add(samplesRaw[i]);
}
long[] queueRaw = queue.elements();
int j = 0;
int k;
for (k = queue.size() - 3; j < k; j += 4) {
final long packed1 = queueRaw[j];
final long packed2 = queueRaw[j + 1];
final long packed3 = queueRaw[j + 2];
final long packed4 = queueRaw[j + 3];
final LevelChunk chunk1 = getChunk(world, packed1);
final LevelChunk chunk2 = packed1 != packed2 ? getChunk(world, packed2) : chunk1;
final LevelChunk chunk3 = packed2 != packed3 ? getChunk(world, packed3) : chunk2;
final LevelChunk chunk4 = packed3 != packed4 ? getChunk(world, packed4) : chunk3;
if (chunk1 != null) tickBlock(world, chunk1, random);
if (chunk2 != null) tickBlock(world, chunk2, random);
if (chunk3 != null) tickBlock(world, chunk3, random);
if (chunk4 != null) tickBlock(world, chunk4, random);
}
for (k = queue.size(); j < k; j++) {
final LevelChunk chunk = getChunk(world, queueRaw[j]);
if (chunk != null) tickBlock(world, chunk, random);
}
weightsSum = 0L;
queue.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) {
int count = chunk.leaf$tickingBlocksCount();
if (count == 0) {
return;
}
OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(count));
if (optionalPos.isEmpty()) {
return;
}
BlockPos pos = BlockPos.of(optionalPos.getAsLong());
BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ());
state.randomTick(world, pos, random);
final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
if (doubleTickFluids) {
final FluidState fluidState = state.getFluidState();
if (fluidState.isRandomlyTicking()) {
fluidState.randomTick(world, pos, random);
}
}
}
public void tickChunk(
RandomSource random,
LevelChunk chunk,
long tickSpeed
) {
if (this.bits == BITS_MAX) {
this.bits = 0;
this.cacheRandom = random.nextLong();
} else {
this.bits += BITS_STEP;
}
if ((this.cacheRandom & (TICK_MASK << bits)) == 0L) {
long count = chunk.leaf$tickingBlocksCount();
if (count != 0L) {
long weight = (TICK_MUL * tickSpeed * count * SCALE) / CHUNK_BLOCKS;
samples.add(chunk.getPos().longKey);
weights.add(weight);
weightsSum += weight;
}
}
}
/**
* @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();
if ((bound & m) == 0L) {
r &= m;
} else {
for (long u = r >>> 1;
u + m - (r = u % bound) < 0L;
u = rng.nextLong() >>> 1)
;
}
return r;
}
}

View File

@@ -20,8 +20,6 @@ public class OptimizedPoweredRails {
private static final int UPDATE_FORCE_PLACE = UPDATE_MOVE_BY_PISTON | UPDATE_KNOWN_SHAPE | UPDATE_CLIENTS;
private static int RAIL_POWER_LIMIT = 8;
private static final Object2BooleanOpenHashMap<BlockPos> CHECKED_POS_POOL = new Object2BooleanOpenHashMap<>();
private static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) {
@@ -36,14 +34,6 @@ public class OptimizedPoweredRails {
);
}
public static int getRailPowerLimit() {
return RAIL_POWER_LIMIT;
}
public static void setRailPowerLimit(int powerLimit) {
RAIL_POWER_LIMIT = powerLimit;
}
public static void updateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) {
boolean shouldBePowered = level.hasNeighborSignal(pos) ||
findPoweredRailSignalFaster(self, level, pos, state, true, 0, CHECKED_POS_POOL) ||
@@ -97,7 +87,7 @@ public class OptimizedPoweredRails {
private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level,
BlockPos pos, BlockState state, boolean searchForward, int distance,
Object2BooleanOpenHashMap<BlockPos> checkedPos) {
if (distance >= RAIL_POWER_LIMIT - 1) return false;
if (distance >= level.purpurConfig.railActivationRange) return false;
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
@@ -203,7 +193,8 @@ public class OptimizedPoweredRails {
private static void setRailPositionsPower(PoweredRailBlock self, Level level, BlockPos pos,
Object2BooleanOpenHashMap<BlockPos> checkedPos, int[] count, int i, Direction dir) {
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
final int railPowerLimit = level.purpurConfig.railActivationRange;
for (int z = 1; z < railPowerLimit; z++) {
BlockPos newPos = pos.relative(dir, z);
BlockState state = level.getBlockState(newPos);
if (checkedPos.containsKey(newPos)) {
@@ -229,7 +220,8 @@ public class OptimizedPoweredRails {
int[] count, int i, Direction dir) {
Object2BooleanOpenHashMap<BlockPos> checkedPos = CHECKED_POS_POOL;
checkedPos.clear();
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
final int railPowerLimit = level.purpurConfig.railActivationRange;
for (int z = 1; z < railPowerLimit; z++) {
BlockPos newPos = pos.relative(dir, z);
BlockState state = level.getBlockState(newPos);
if (!state.is(self) || !state.getValue(POWERED) || level.hasNeighborSignal(newPos) ||