From 1431eff510a3ac1725ec82d52f5c253b4b7932d9 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 3 May 2025 07:50:33 -0400 Subject: [PATCH 01/81] [ci skip] cleanup --- README.md | 2 +- .../features/0154-Async-target-finding.patch | 15 +++++++-------- .../leaf/util/map/ConcurrentLongHashSet.java | 5 ++++- .../linearpaper/region/LinearRegionFile.java | 3 +-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9176fba9..b471e356 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ You can find the latest successful build in [GitHub Action](https://github.com/W **Please note Java >= 21 is required.** ## 📄 Documentation -Documentation on how to use/configure Leaf: [leafmc.one/docs/](https://www.leafmc.one/docs/) +Documentation on how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs) ## 📦 Building Building a Paperclip JAR for distribution: diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index 7d340622..822fb0ef 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -1776,7 +1776,7 @@ index 002d3c0d8b1107a275020d5c582c37e9a5c536ee..6fa0b8defbd1d06b3bf5d9b32ffd08f3 // Gale start - Petal - reduce line of sight cache lookups - merge sets int cached = this.seen.get(id); diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java -index 90452f0945e761077608692877677f522d38bccd..5c0f0ae7bb1aa379487816b115e43140dd27aadc 100644 +index 90452f0945e761077608692877677f522d38bccd..54044ff16e0c2963b2220e4fb4932fe0802aa178 100644 --- a/net/minecraft/world/entity/animal/Fox.java +++ b/net/minecraft/world/entity/animal/Fox.java @@ -849,13 +849,18 @@ public class Fox extends Animal implements VariantHolder { @@ -1795,7 +1795,7 @@ index 90452f0945e761077608692877677f522d38bccd..5c0f0ae7bb1aa379487816b115e43140 this.trustedLastHurtBy = livingEntity.getLastHurtByMob(); int lastHurtByMobTimestamp = livingEntity.getLastHurtByMobTimestamp(); - return lastHurtByMobTimestamp != this.timestamp && this.canAttack(this.trustedLastHurtBy, this.targetConditions); -+ return lastHurtByMobTimestamp != this.timestamp && this.canAttack(this.trustedLastHurtBy, this.getTargetConditions()); // Leaf - Async target finding - diff on change ++ return lastHurtByMobTimestamp != this.timestamp && this.canAttack(this.trustedLastHurtBy, this.getTargetConditions()); // Leaf - Async target finding } } @@ -1987,18 +1987,18 @@ index ae4ee948971e931e4fdc4ec2187f5182195c626c..f4fa19c6352e44a624e81dc201b1d7d7 static class StriderPathNavigation extends GroundPathNavigation { diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index ac7729d1caa80155697bfe0e8646e4eda5d1780e..ae765e97b4dbd2a932de32c95fc1d8355841b04c 100644 +index ac7729d1caa80155697bfe0e8646e4eda5d1780e..2518ea7a9d2122ff55ab6546d21d7ed6cc933090 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -1548,6 +1548,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - this.moonrise$midTickTasks(); +@@ -1549,6 +1549,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - do not bother with condition work / make configurable -+ ((ServerLevel) this).leafMidTickTasks(); // Leaf - Async target finding // Paper end - rewrite chunk system ++ ((ServerLevel) this).leafMidTickTasks(); // Leaf - Async target finding } } -@@ -1817,9 +1818,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals +@@ -1817,9 +1818,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Override public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { @@ -2008,7 +2008,6 @@ index ac7729d1caa80155697bfe0e8646e4eda5d1780e..ae765e97b4dbd2a932de32c95fc1d835 + // Leaf start - Async target finding + // if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) + // ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) -+ + // List list = Lists.newArrayList(); // Leaf - Async target finding - unused + // Leaf end - Async target finding diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ConcurrentLongHashSet.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ConcurrentLongHashSet.java index b699f723..4d9fea75 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/ConcurrentLongHashSet.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/ConcurrentLongHashSet.java @@ -1,6 +1,9 @@ package org.dreeam.leaf.util.map; -import it.unimi.dsi.fastutil.longs.*; +import it.unimi.dsi.fastutil.longs.LongCollection; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; import org.jetbrains.annotations.NotNull; import java.util.Arrays; diff --git a/leaf-server/src/main/java/org/stupidcraft/linearpaper/region/LinearRegionFile.java b/leaf-server/src/main/java/org/stupidcraft/linearpaper/region/LinearRegionFile.java index 9bdbed24..391a9871 100644 --- a/leaf-server/src/main/java/org/stupidcraft/linearpaper/region/LinearRegionFile.java +++ b/leaf-server/src/main/java/org/stupidcraft/linearpaper/region/LinearRegionFile.java @@ -147,7 +147,7 @@ public class LinearRegionFile implements IRegionFile { private synchronized void save() throws IOException { if (MinecraftServer.getServer().hasStopped()) { - // Crazy - save only once on shutdown + // Save only once on shutdown if (!closed) return; } @@ -224,7 +224,6 @@ public class LinearRegionFile implements IRegionFile { LOGGER.error("Chunk write IOException {} {}", e, this.path); } - // Crazy: bruh if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - this.lastFlushed) >= (RegionFormatConfig.linearFlushFrequency)) { this.flushWrapper(); } From 1ffa315c13e108575776db508eb6d1df90f52461 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 2 May 2025 18:33:48 -0700 Subject: [PATCH 02/81] Reduce AsyncGoal content switching --- .../leaf/async/AsyncPlayerDataSaving.java | 3 +- .../leaf/async/ai/AsyncGoalExecutor.java | 61 +++++++++---------- .../dreeam/leaf/async/ai/AsyncGoalThread.java | 27 +++----- .../dreeam/leaf/util/queue/SpscIntQueue.java | 6 +- 4 files changed, 42 insertions(+), 55 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java index 40f552d1..3df32778 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncPlayerDataSaving.java @@ -1,5 +1,6 @@ package org.dreeam.leaf.async; +import net.minecraft.Util; import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave; import java.util.Optional; @@ -17,7 +18,7 @@ public class AsyncPlayerDataSaving { new com.google.common.util.concurrent.ThreadFactoryBuilder() .setPriority(Thread.NORM_PRIORITY - 2) .setNameFormat("Leaf IO Thread") - .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)) + .setUncaughtExceptionHandler(Util::onThreadException) .build(), new ThreadPoolExecutor.DiscardPolicy() ); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index b6a4c632..646dc638 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -17,20 +17,19 @@ public class AsyncGoalExecutor { protected final SpscIntQueue queue; protected final SpscIntQueue wake; private final AsyncGoalThread thread; - private final ServerLevel serverLevel; + private final ServerLevel world; private boolean dirty = false; private long tickCount = 0L; - private static final int SPIN_LIMIT = 100; - public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel serverLevel) { - this.serverLevel = serverLevel; + public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) { + this.world = world; this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize); this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize); this.thread = thread; } boolean wake(int id) { - Entity entity = this.serverLevel.getEntities().get(id); + Entity entity = this.world.getEntities().get(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } @@ -40,19 +39,13 @@ public class AsyncGoalExecutor { } public final void submit(int entityId) { - if (!this.queue.send(entityId)) { - int spinCount = 0; - while (!this.queue.send(entityId)) { - spinCount++; - // Unpark the thread after some spinning to help clear the queue - if (spinCount > SPIN_LIMIT) { - unpark(); - spinCount = 0; - } - Thread.onSpinWait(); - } - } dirty = true; + if (!this.queue.send(entityId)) { + unpark(); + do { + wake(entityId); + } while (poll(entityId)); + } } public final void unpark() { @@ -61,27 +54,31 @@ public class AsyncGoalExecutor { } public final void midTick() { - boolean didWork = false; while (true) { - int id = this.wake.recv(); - if (id == Integer.MAX_VALUE) { + Integer id = this.wake.recv(); + if (id == null) { break; } - didWork = true; - Entity entity = this.serverLevel.getEntities().get(id); - if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) { - continue; - } - - mob.tickingTarget = true; - boolean a = mob.targetSelector.poll(); - mob.tickingTarget = false; - boolean b = mob.goalSelector.poll(); - if (a || b) { + if (poll(id)) { submit(id); } } - if (didWork || (tickCount & 15L) == 0L) unpark(); + if ((tickCount & 7L) == 7L) { + unpark(); + } tickCount += 1; } + + private boolean poll(int id) { + Entity entity = this.world.getEntities().get(id); + if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) { + return false; + } + + mob.tickingTarget = true; + boolean a = mob.targetSelector.poll(); + mob.tickingTarget = false; + boolean b = mob.goalSelector.poll(); + return a || b; + } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java index d3d236c2..6dbf6474 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java @@ -8,8 +8,6 @@ import java.util.concurrent.locks.LockSupport; public class AsyncGoalThread extends Thread { - private static final int SPIN_TRIES = 1000; - public AsyncGoalThread(final MinecraftServer server) { super(() -> run(server), "Leaf Async Goal Thread"); this.setDaemon(false); @@ -19,38 +17,27 @@ public class AsyncGoalThread extends Thread { } private static void run(MinecraftServer server) { - int emptySpins = 0; - while (server.isRunning()) { - boolean didWork = false; + boolean retry = false; for (ServerLevel level : server.getAllLevels()) { var exec = level.asyncGoalExecutor; - boolean levelWork = false; while (true) { - int id = exec.queue.recv(); - if (id == Integer.MAX_VALUE) { + Integer id = exec.queue.recv(); + if (id == null) { break; } - levelWork = true; + retry = true; if (exec.wake(id)) { while (!exec.wake.send(id)) { Thread.onSpinWait(); } } } - didWork |= levelWork; } - // Adaptive parking - if (didWork) { - emptySpins = 0; // Reset counter when work was done + if (retry) { + Thread.yield(); } else { - emptySpins++; - if (emptySpins > SPIN_TRIES) { - LockSupport.park(); // Only park after several empty spins - emptySpins = 0; - } else { - Thread.onSpinWait(); // Yield to other threads but don't park - } + LockSupport.park(); } } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java index 3368c34e..7e2f243a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java @@ -1,5 +1,7 @@ package org.dreeam.leaf.util.queue; +import org.jetbrains.annotations.Nullable; + /// Lock-free Single Producer Single Consumer Queue public class SpscIntQueue { @@ -33,14 +35,14 @@ public class SpscIntQueue { } - public final int recv() { + public final @Nullable Integer recv() { final int idx = consumerIdx.getOpaque(); int cachedIdx = producerCachedIdx.getPlain(); if (idx == cachedIdx) { cachedIdx = producerIdx.getAcquire(); producerCachedIdx.setPlain(cachedIdx); if (idx == cachedIdx) { - return Integer.MAX_VALUE; + return null; } } int e = data[idx]; From cc2781e7faf875964c6c6c336ff85323bde30665 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 00:22:02 +0800 Subject: [PATCH 03/81] don't submit running goal --- .../features/0154-Async-target-finding.patch | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index 822fb0ef..d41c09d8 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -190,7 +190,7 @@ index 0903508d2cd3c78602e62dbcff4aa70285bc4c4f..e99fd55a90c68cc96701e5291219d1d0 // Paper start - Folia schedulers diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..7b4330c15a2bdf63882c4ed025f2b426592a9bba 100644 +index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff339241705d63 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -144,6 +144,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -206,21 +206,22 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..7b4330c15a2bdf63882c4ed025f2b426 protected Mob(EntityType entityType, Level level) { super(entityType, level); -@@ -225,12 +231,21 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -225,12 +231,22 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab } // Paper end - Skip AI during inactive ticks for non-aware mobs boolean isThrottled = org.dreeam.leaf.config.modules.opt.ThrottleInactiveGoalSelectorTick.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking -+ this.tickingTarget = false; // Leaf - Async target finding ++ // Leaf start - Async target finding ++ boolean running = this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null; ++ this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); } -+ this.tickingTarget = true; // Leaf - Async target finding ++ this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority this.targetSelector.tick(); } -+ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null) { ++ if (!running && (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null)) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } @@ -228,11 +229,12 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..7b4330c15a2bdf63882c4ed025f2b426 } // Paper end -@@ -914,17 +929,28 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -914,17 +930,29 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab // Paper end - Allow nerfed mobs to jump and float this.sensing.tick(); int i = this.tickCount + this.getId(); + // Leaf start - Async target finding ++ boolean running = this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null; if (i % 2 != 0 && this.tickCount > 1) { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking @@ -249,7 +251,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..7b4330c15a2bdf63882c4ed025f2b426 this.goalSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null) { ++ if (!running && (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null)) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } From 14e54573086e381a735a5156c3fc398f2f7956cf Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 04:31:16 -0700 Subject: [PATCH 04/81] fix send null attribute --- .../features/0085-Multithreaded-Tracker.patch | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index f322c327..de2e323e 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -177,7 +177,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index d8298c7925e3bcea07ead4d438478cc51abcfa16..865e98a7afe116fe42c5ad0d8a96adbd92bc8dc0 100644 +index d8298c7925e3bcea07ead4d438478cc51abcfa16..a13ce614722d26dcbb2f7f3ceeb68915bbaec304 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -110,8 +110,16 @@ public class ServerEntity { @@ -199,28 +199,28 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..865e98a7afe116fe42c5ad0d8a96adbd } } ); -@@ -435,15 +443,18 @@ public class ServerEntity { +@@ -434,15 +442,17 @@ public class ServerEntity { + if (this.entity instanceof LivingEntity) { Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); - if (!attributesToSync.isEmpty()) { -+ // Leaf start - petal - Multithreaded tracker - send in main thread -+ final Set copy = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(attributesToSync); +- if (!attributesToSync.isEmpty()) { ++ // Leaf start - petal - Multithreaded tracker - send in the main thread ++ var clonedAttributes = new it.unimi.dsi.fastutil.objects.ObjectArraySet<>(attributesToSync); ++ if (!clonedAttributes.isEmpty()) { // CraftBukkit start - Send scaled max health if (this.entity instanceof ServerPlayer serverPlayer) { - serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(copy, false); ++ serverPlayer.getBukkitEntity().injectScaledMaxHealth(clonedAttributes, false); } // CraftBukkit end - this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); -+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy)); -+ // Leaf end - petal - Multithreaded tracker - send in main thread ++ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), clonedAttributes)); // Leaf - petal - Multithreaded tracker - send in the main thread } - -- attributesToSync.clear(); -+ ((LivingEntity)this.entity).getAttributes().getAttributesToSync().clear(); // Leaf - Multithreaded tracker +- ++ // Leaf end - petal - Multithreaded tracker - send in the main thread + attributesToSync.clear(); } } - diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index d6ebc25dc5f04194edde5ad3a1166113e5542a1d..49cbdf014d0626b36eb4c451b6de09508822b7fd 100644 --- a/net/minecraft/server/level/ServerLevel.java @@ -256,6 +256,27 @@ index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7e // Paper start - Prevent teleporting dead entities if (this.player.isRemoved()) { LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); +diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java +index f8419dde44ebc7324e783f8bee42132d5ec973c3..db076814f44f18b549020a38c86587e7b7525ce5 100644 +--- a/net/minecraft/world/entity/ai/attributes/Attribute.java ++++ b/net/minecraft/world/entity/ai/attributes/Attribute.java +@@ -17,10 +17,16 @@ public class Attribute { + private final String descriptionId; + private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; + ++ // Leaf start - optimize AttributeMap ++ private final static java.util.concurrent.atomic.AtomicInteger COUNTER = new java.util.concurrent.atomic.AtomicInteger(0); ++ public final int uid; ++ + protected Attribute(String descriptionId, double defaultValue) { + this.defaultValue = defaultValue; + this.descriptionId = descriptionId; ++ this.uid = COUNTER.getAndAdd(1); + } ++ // Leaf end - optimize AttributeMap + + public double getDefaultValue() { + return this.defaultValue; diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java index 8013594bb4844e7a8abf28123958e7f632d39341..80c703fe85ec21e4d218823504f4bbf7826b2fa6 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java From 281a81784e9dce52a4d85f8f0a637d2dc697c608 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 04:37:13 -0700 Subject: [PATCH 05/81] [ci skip] cleanup --- .../features/0085-Multithreaded-Tracker.patch | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index de2e323e..337ace2e 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -256,27 +256,6 @@ index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7e // Paper start - Prevent teleporting dead entities if (this.player.isRemoved()) { LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); -diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java -index f8419dde44ebc7324e783f8bee42132d5ec973c3..db076814f44f18b549020a38c86587e7b7525ce5 100644 ---- a/net/minecraft/world/entity/ai/attributes/Attribute.java -+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java -@@ -17,10 +17,16 @@ public class Attribute { - private final String descriptionId; - private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; - -+ // Leaf start - optimize AttributeMap -+ private final static java.util.concurrent.atomic.AtomicInteger COUNTER = new java.util.concurrent.atomic.AtomicInteger(0); -+ public final int uid; -+ - protected Attribute(String descriptionId, double defaultValue) { - this.defaultValue = defaultValue; - this.descriptionId = descriptionId; -+ this.uid = COUNTER.getAndAdd(1); - } -+ // Leaf end - optimize AttributeMap - - public double getDefaultValue() { - return this.defaultValue; diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java index 8013594bb4844e7a8abf28123958e7f632d39341..80c703fe85ec21e4d218823504f4bbf7826b2fa6 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java From ce01c9bb1fcd18980663dfa171caff5b34fd3775 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 23:18:03 +0800 Subject: [PATCH 06/81] fix bundle crash exploit --- .../features/0162-paw-optimization.patch | 8 +++---- .../0165-Fix-paper-s-bundle-exploit.patch | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch diff --git a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch b/leaf-server/minecraft-patches/features/0162-paw-optimization.patch index f3e96166..aa641437 100644 --- a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0162-paw-optimization.patch @@ -77,7 +77,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 d75f85208da0c7424fc95ae0d8ebb0a725dda0a7..4c0a6dcee5e63f788e532208ef3485726eedf8cc 100644 +index 0860a700106e8c1afe58c77150a0f3aee8393fdd..8e5414becccc921db39e4e6eebeb054d3af94291 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -504,9 +504,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -126,7 +126,7 @@ index fbfb35dad8b07c31f967d33fb04cfcfc94557d72..ba1dd51e7187a80e8438e46383257c22 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 a81983182ee3e3b874ba83ddf9bbc6ea772a2997..4221e5322fa3a3ff6ab53946aa71d54144d2c4b2 100644 +index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..124624d9c8cc292fcedb5652542b7d9da4e5d228 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -1143,31 +1143,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -192,7 +192,7 @@ index a81983182ee3e3b874ba83ddf9bbc6ea772a2997..4221e5322fa3a3ff6ab53946aa71d541 } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4833,9 +4792,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4832,9 +4791,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -202,7 +202,7 @@ index a81983182ee3e3b874ba83ddf9bbc6ea772a2997..4221e5322fa3a3ff6ab53946aa71d541 } public void addDeltaMovement(Vec3 addend) { -@@ -4941,9 +4898,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4940,9 +4897,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix MC-4 if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch b/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch new file mode 100644 index 00000000..45324f89 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 4 May 2025 13:49:50 +0200 +Subject: [PATCH] Fix paper's bundle exploit + +Original log: https://discord.com/channels/1145991395388162119/1368528332605620224/1368531526261932167 + +diff --git a/net/minecraft/world/item/component/BundleContents.java b/net/minecraft/world/item/component/BundleContents.java +index d6326dcb3700bc69c6c061ca8544b212f909277b..03ef306164c719b5c9b260224e2813ffdad4ea16 100644 +--- a/net/minecraft/world/item/component/BundleContents.java ++++ b/net/minecraft/world/item/component/BundleContents.java +@@ -233,7 +233,8 @@ public final class BundleContents implements TooltipComponent { + if (this.items.isEmpty()) { + return null; + } else { +- int i = !indexIsOutsideAllowedBounds(this.selectedItem) ? this.selectedItem : 0; // Paper ++ int i = 0; ++ if (this.selectedItem >= 0 && this.selectedItem < this.items.size()) {i = this.selectedItem;} + ItemStack itemStack = this.items.remove(i).copy(); + this.weight = this.weight.subtract(BundleContents.getWeight(itemStack).multiplyBy(Fraction.getFraction(itemStack.getCount(), 1))); + this.toggleSelectedItem(-1); From 3ae994e70798ea09b77826d6445c009ef6abc344 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 23:19:53 +0800 Subject: [PATCH 07/81] remove box on SpscIntQueue#recv --- .../java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java | 6 ++++-- .../java/org/dreeam/leaf/async/ai/AsyncGoalThread.java | 6 ++++-- .../java/org/dreeam/leaf/util/queue/SpscIntQueue.java | 8 +++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index 646dc638..9a656f4d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.Logger; 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 { @@ -55,10 +56,11 @@ public class AsyncGoalExecutor { public final void midTick() { while (true) { - Integer id = this.wake.recv(); - if (id == null) { + OptionalInt result = this.wake.recv(); + if (result.isEmpty()) { break; } + int id = result.getAsInt(); if (poll(id)) { submit(id); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java index 6dbf6474..df3e3a0e 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java @@ -4,6 +4,7 @@ import net.minecraft.Util; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; +import java.util.OptionalInt; import java.util.concurrent.locks.LockSupport; public class AsyncGoalThread extends Thread { @@ -22,10 +23,11 @@ public class AsyncGoalThread extends Thread { for (ServerLevel level : server.getAllLevels()) { var exec = level.asyncGoalExecutor; while (true) { - Integer id = exec.queue.recv(); - if (id == null) { + OptionalInt result = exec.queue.recv(); + if (result.isEmpty()) { break; } + int id = result.getAsInt(); retry = true; if (exec.wake(id)) { while (!exec.wake.send(id)) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java index 7e2f243a..e880136d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java @@ -2,6 +2,8 @@ package org.dreeam.leaf.util.queue; import org.jetbrains.annotations.Nullable; +import java.util.OptionalInt; + /// Lock-free Single Producer Single Consumer Queue public class SpscIntQueue { @@ -35,14 +37,14 @@ public class SpscIntQueue { } - public final @Nullable Integer recv() { + public final OptionalInt recv() { final int idx = consumerIdx.getOpaque(); int cachedIdx = producerCachedIdx.getPlain(); if (idx == cachedIdx) { cachedIdx = producerIdx.getAcquire(); producerCachedIdx.setPlain(cachedIdx); if (idx == cachedIdx) { - return null; + return OptionalInt.empty(); } } int e = data[idx]; @@ -51,7 +53,7 @@ public class SpscIntQueue { nextIdx = 0; } consumerIdx.setRelease(nextIdx); - return e; + return OptionalInt.of(e); } public final int size() { From 65489c6f46a1039bcac80c51bf83650319b7a6c7 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 4 May 2025 23:25:21 +0800 Subject: [PATCH 08/81] revert "Fix paper's bundle exploit" --- .../0165-Fix-paper-s-bundle-exploit.patch | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch diff --git a/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch b/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch deleted file mode 100644 index 45324f89..00000000 --- a/leaf-server/minecraft-patches/features/0165-Fix-paper-s-bundle-exploit.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Sun, 4 May 2025 13:49:50 +0200 -Subject: [PATCH] Fix paper's bundle exploit - -Original log: https://discord.com/channels/1145991395388162119/1368528332605620224/1368531526261932167 - -diff --git a/net/minecraft/world/item/component/BundleContents.java b/net/minecraft/world/item/component/BundleContents.java -index d6326dcb3700bc69c6c061ca8544b212f909277b..03ef306164c719b5c9b260224e2813ffdad4ea16 100644 ---- a/net/minecraft/world/item/component/BundleContents.java -+++ b/net/minecraft/world/item/component/BundleContents.java -@@ -233,7 +233,8 @@ public final class BundleContents implements TooltipComponent { - if (this.items.isEmpty()) { - return null; - } else { -- int i = !indexIsOutsideAllowedBounds(this.selectedItem) ? this.selectedItem : 0; // Paper -+ int i = 0; -+ if (this.selectedItem >= 0 && this.selectedItem < this.items.size()) {i = this.selectedItem;} - ItemStack itemStack = this.items.remove(i).copy(); - this.weight = this.weight.subtract(BundleContents.getWeight(itemStack).multiplyBy(Fraction.getFraction(itemStack.getCount(), 1))); - this.toggleSelectedItem(-1); From 89a89fa7713242b0730c1236337aac2e8fe4bf52 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 4 May 2025 23:31:34 +0200 Subject: [PATCH 09/81] remove PWT console print --- .../0134-SparklyPaper-Parallel-world-ticking.patch | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 35e26162..6b316718 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -704,7 +704,7 @@ index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcef // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..0b8b4658dbbad1bacc13e97b4fc0cdcea7e36a06 100644 +index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..3591de34443069f3f163f8d17df6372c3068611d 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { @@ -716,14 +716,12 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..0b8b4658dbbad1bacc13e97b4fc0cdce player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed GameProfile gameProfile = player.getGameProfile(); -@@ -891,6 +893,17 @@ public abstract class PlayerList { +@@ -891,6 +893,15 @@ public abstract class PlayerList { return this.respawn(player, keepInventory, reason, eventReason, null); } public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, org.bukkit.Location location) { + // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ // SparklyPaper - parallel world ticking (additional concurrency issues logs) -+ System.out.println("respawning player - current player container is " + player.containerMenu + " but their inventory is " + player.inventoryMenu); + if (location != null) // Leaf - THIS CAN BE NULL; see PlayerList::respawn(ServerPlayer, boolean, Entity.RemovalReason, PlayerRespawnEvent.RespawnReason) + ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + player.serverLevel().getWorld().getName() + " to world " + location.getWorld().getName()); + else @@ -734,7 +732,7 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..0b8b4658dbbad1bacc13e97b4fc0cdce player.stopRiding(); // CraftBukkit this.players.remove(player); this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -902,6 +915,7 @@ public abstract class PlayerList { +@@ -902,6 +913,7 @@ public abstract class PlayerList { ServerPlayer serverPlayer = player; Level fromWorld = player.level(); player.wonGame = false; From 41664455a4ef781652f500cccf9d2f7095d96cac Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Mon, 5 May 2025 15:18:30 +0800 Subject: [PATCH 10/81] extend SpscIntQueue padding size to 252 --- .../dreeam/leaf/util/queue/SpscIntQueue.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java index e880136d..2aede1fb 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/queue/SpscIntQueue.java @@ -1,7 +1,5 @@ package org.dreeam.leaf.util.queue; -import org.jetbrains.annotations.Nullable; - import java.util.OptionalInt; /// Lock-free Single Producer Single Consumer Queue @@ -61,8 +59,28 @@ public class SpscIntQueue { } static class PaddedAtomicInteger extends java.util.concurrent.atomic.AtomicInteger { + // @formatter:off @SuppressWarnings("unused") - private int i1, i2, i3, i4, i5, i6, i7, i8, - i9, i10, i11, i12, i13, i14, i15; + private byte + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, + j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15, + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, + l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, + + m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, + n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, + o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12, o13, o14, o15, + p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + + q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, q13, q14, q15, + r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, + s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, + t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, + + u0, u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11, u12, u13, u14, u15, + v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, + x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11; + // @formatter:on } } From 974ede5f87ffb243ec1790849980dbe45b22b859 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 7 May 2025 00:37:31 +0900 Subject: [PATCH 11/81] Do A Barrel Roll Protocol (#315) * Do A Barrel Roll Protocol * cleanup * [ci skip] cleanup * [ci skip] cleanup * [ci skip] rename patch --- .../features/0165-Protocol-Core.patch | 84 +++++ .../modules/network/ProtocolSupport.java | 30 ++ .../leaf/protocol/DoABarrelRollPackets.java | 148 +++++++++ .../leaf/protocol/DoABarrelRollProtocol.java | 298 ++++++++++++++++++ .../leaf/protocol/LeafCustomPayload.java | 10 + .../org/dreeam/leaf/protocol/Protocol.java | 19 ++ .../org/dreeam/leaf/protocol/Protocols.java | 97 ++++++ 7 files changed, 686 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0165-Protocol-Core.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollPackets.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java diff --git a/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch new file mode 100644 index 00000000..4a11921d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch @@ -0,0 +1,84 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 6 May 2025 17:44:16 +0900 +Subject: [PATCH] Protocol Core + + +diff --git a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java +index 7e19dfe90a63ff26f03b95891dacb7360bba5a3c..d20ffa172227f85b9fd6ac5e2766f6ebd2d07638 100644 +--- a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java ++++ b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java +@@ -47,6 +47,12 @@ public interface CustomPacketPayload { + return; + } + // Leaves end - protocol core ++ // Leaf start - protocol ++ if (value instanceof org.dreeam.leaf.protocol.LeafCustomPayload payload) { ++ org.dreeam.leaf.protocol.Protocols.write(buffer, payload); ++ return; ++ } ++ // Leaf end - protocol + this.writeCap(buffer, value.type(), value); + } + +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 98af1ad020a003db66d7319f33d43deec315aec5..e04a6db55d936277f2a852374f11d483d79a90ed 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1839,6 +1839,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop LeafCustomPayload.@NotNull Type createType(String path) { + return new LeafCustomPayload.Type<>(ResourceLocation.fromNamespaceAndPath(DoABarrelRollProtocol.NAMESPACE, path)); + } + + public record ConfigResponseC2SPacket(int protocolVersion, boolean success) implements LeafCustomPayload { + public static final Type TYPE = createType("config_response"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, ConfigResponseC2SPacket::protocolVersion, + ByteBufCodecs.BOOL, ConfigResponseC2SPacket::success, + ConfigResponseC2SPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public record ConfigSyncS2CPacket(int protocolVersion, + LimitedModConfigServer applicableConfig, + boolean isLimited, + ModConfigServer fullConfig + ) implements LeafCustomPayload { + public static final Type TYPE = createType("config_sync"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, ConfigSyncS2CPacket::protocolVersion, + LimitedModConfigServer.getCodec(), ConfigSyncS2CPacket::applicableConfig, + ByteBufCodecs.BOOL, ConfigSyncS2CPacket::isLimited, + ModConfigServer.PACKET_CODEC, ConfigSyncS2CPacket::fullConfig, + ConfigSyncS2CPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public record ConfigUpdateAckS2CPacket(int protocolVersion, boolean success) implements LeafCustomPayload { + public static final Type TYPE = createType("config_update_ack"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, ConfigUpdateAckS2CPacket::protocolVersion, + ByteBufCodecs.BOOL, ConfigUpdateAckS2CPacket::success, + ConfigUpdateAckS2CPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public record ConfigUpdateC2SPacket(int protocolVersion, ModConfigServer config) implements LeafCustomPayload { + public static final Type TYPE = createType("config_update"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, ConfigUpdateC2SPacket::protocolVersion, + ModConfigServer.PACKET_CODEC, ConfigUpdateC2SPacket::config, + ConfigUpdateC2SPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public record RollSyncC2SPacket(boolean rolling, float roll) implements LeafCustomPayload { + public static final Type TYPE = createType("roll_sync"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, RollSyncC2SPacket::rolling, + ByteBufCodecs.FLOAT, RollSyncC2SPacket::roll, + RollSyncC2SPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public record RollSyncS2CPacket(int entityId, boolean rolling, float roll) implements LeafCustomPayload { + public static final Type TYPE = createType("roll_sync"); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, RollSyncS2CPacket::entityId, + ByteBufCodecs.BOOL, RollSyncS2CPacket::rolling, + ByteBufCodecs.FLOAT, RollSyncS2CPacket::roll, + RollSyncS2CPacket::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + } + + public interface LimitedModConfigServer { + boolean allowThrusting(); + + boolean forceEnabled(); + + static StreamCodec getCodec() { + return StreamCodec.composite( + ByteBufCodecs.BOOL, LimitedModConfigServer::allowThrusting, + ByteBufCodecs.BOOL, LimitedModConfigServer::forceEnabled, + Impl::new + ); + } + + record Impl(boolean allowThrusting, boolean forceEnabled) implements LimitedModConfigServer { + } + } + + public record ModConfigServer(boolean allowThrusting, + boolean forceEnabled, + boolean forceInstalled, + int installedTimeout, + KineticDamage kineticDamage + ) implements LimitedModConfigServer { + public static final StreamCodec PACKET_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, ModConfigServer::allowThrusting, + ByteBufCodecs.BOOL, ModConfigServer::forceEnabled, + ByteBufCodecs.BOOL, ModConfigServer::forceInstalled, + ByteBufCodecs.INT, ModConfigServer::installedTimeout, + KineticDamage.CODEC, ModConfigServer::kineticDamage, + ModConfigServer::new + ); + } + + public enum KineticDamage { + VANILLA, + HIGH_SPEED, + NONE, + INSTANT_KILL; + + public static final StreamCodec CODEC = + ByteBufCodecs.STRING_UTF8.map(KineticDamage::valueOf, KineticDamage::name); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java new file mode 100644 index 00000000..dca31a84 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -0,0 +1,298 @@ +package org.dreeam.leaf.protocol; + +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.objects.*; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.server.network.ServerPlayerConnection; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.event.player.PlayerKickEvent; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.*; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.OptionalInt; + +public class DoABarrelRollProtocol implements Protocol { + protected static final String NAMESPACE = "do_a_barrel_roll"; + private static final Logger LOGGER = LogManager.getLogger(NAMESPACE); + private static final int PROTOCOL_VERSION = 4; + private static final ModConfigServer DEFAULT = new ModConfigServer(false, false, false, 40, KineticDamage.VANILLA); + private static final Component SYNC_TIMEOUT_MESSAGE = Component.literal("Please install Do a Barrel Roll 2.4.0 or later to play on this server."); + private static DoABarrelRollProtocol INSTANCE = null; + + private final List> c2s = ImmutableList.of( + new Protocols.TypeAndCodec<>(ConfigUpdateC2SPacket.TYPE, ConfigUpdateC2SPacket.STREAM_CODEC), + new Protocols.TypeAndCodec<>(ConfigResponseC2SPacket.TYPE, ConfigResponseC2SPacket.STREAM_CODEC), + new Protocols.TypeAndCodec<>(RollSyncC2SPacket.TYPE, RollSyncC2SPacket.STREAM_CODEC)); + + private final List> s2c = ImmutableList.of( + new Protocols.TypeAndCodec<>(ConfigUpdateAckS2CPacket.TYPE, ConfigUpdateAckS2CPacket.STREAM_CODEC), + new Protocols.TypeAndCodec<>(ConfigSyncS2CPacket.TYPE, ConfigSyncS2CPacket.STREAM_CODEC), + new Protocols.TypeAndCodec<>(RollSyncS2CPacket.TYPE, RollSyncS2CPacket.STREAM_CODEC) + ); + + private ModConfigServer config = DEFAULT; + private boolean configUpdated = false; + + private final Reference2ReferenceMap syncStates = new Reference2ReferenceOpenHashMap<>(); + private final Reference2ReferenceMap scheduledKicks = new Reference2ReferenceOpenHashMap<>(); + public final Reference2BooleanMap isRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); + public final Reference2FloatMap rollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>()); + public final Reference2BooleanMap lastIsRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); + public final Reference2FloatMap lastRollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>()); + + public static void deinit() { + if (INSTANCE != null) { + INSTANCE = null; + Protocols.unregister(INSTANCE); + } + } + + public static void init( + boolean allowThrusting, + boolean forceEnabled, + boolean forceInstalled, + int installedTimeout, + KineticDamage kineticDamage + ) { + if (INSTANCE == null) { + INSTANCE = new DoABarrelRollProtocol(); + Protocols.register(INSTANCE); + } + INSTANCE.config = new ModConfigServer(allowThrusting, forceEnabled, forceInstalled, installedTimeout, kineticDamage); + INSTANCE.configUpdated = true; + } + + @Override + public String namespace() { + return NAMESPACE; + } + + @Override + public List> c2s() { + return c2s; + } + + @Override + public List> s2c() { + return s2c; + } + + @Override + public void handle(ServerPlayer player, @NotNull LeafCustomPayload payload) { + switch (payload) { + case ConfigUpdateC2SPacket ignored -> + player.connection.send(Protocols.createPacket(new ConfigUpdateAckS2CPacket(PROTOCOL_VERSION, false))); + case ConfigResponseC2SPacket configResponseC2SPacket -> { + var reply = clientReplied(player.connection, configResponseC2SPacket); + if (reply == HandshakeState.RESEND) { + sendHandshake(player); + } + } + case RollSyncC2SPacket rollSyncC2SPacket -> { + var state = getHandshakeState(player.connection); + if (state.state != HandshakeState.ACCEPTED) { + return; + } + var rolling = rollSyncC2SPacket.rolling(); + var roll = rollSyncC2SPacket.roll(); + isRollingMap.put(player.connection, rolling); + if (Float.isInfinite(roll)) { + roll = 0.0F; + } + rollMap.put(player.connection, roll); + } + default -> { + } + } + } + + @Override + public void disconnected(ServerPlayer player) { + final var handler = player.connection; + syncStates.remove(handler); + isRollingMap.removeBoolean(handler); + rollMap.removeFloat(handler); + lastIsRollingMap.removeBoolean(handler); + lastRollMap.removeFloat(handler); + } + + @Override + public void tickTracker(ServerPlayer player) { + if (!isRollingMap.containsKey(player.connection)) { + return; + } + + var isRolling = isRollingMap.getBoolean(player.connection); + var roll = rollMap.getFloat(player.connection); + var lastIsRolling = lastIsRollingMap.getBoolean(player.connection); + var lastRoll = lastRollMap.getFloat(player.connection); + if (isRolling == lastIsRolling && roll == lastRoll) { + return; + } + var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll); + var packet = Protocols.createPacket(payload); + for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy.toArray(new ServerPlayerConnection[0])) { + if (seenBy instanceof ServerGamePacketListenerImpl conn + && getHandshakeState(conn).state == HandshakeState.ACCEPTED) { + seenBy.send(packet); + } + } + lastIsRollingMap.put(player.connection, isRolling); + lastRollMap.put(player.connection, roll); + } + + @Override + public void tickPlayer(ServerPlayer player) { + if (getHandshakeState(player.connection).state == HandshakeState.NOT_SENT) { + sendHandshake(player); + } + if (!isRollingMap.containsKey(player.connection)) { + return; + } + if (!isRollingMap.getBoolean(player.connection)) { + rollMap.put(player.connection, 0.0F); + } + } + + @Override + public void tickServer(MinecraftServer server) { + var it = scheduledKicks.entrySet().iterator(); + while (it.hasNext()) { + var entry = it.next(); + if (entry.getValue().isDone()) { + it.remove(); + } else { + entry.getValue().tick(); + } + } + + if (configUpdated) { + configUpdated = false; + for (ServerPlayer player : server.getPlayerList().players) { + sendHandshake(player); + } + } + } + + private OptionalInt getSyncTimeout(ModConfigServer config) { + return config.forceInstalled() ? OptionalInt.of(config.installedTimeout()) : OptionalInt.empty(); + } + + private void sendHandshake(ServerPlayer player) { + player.connection.send(Protocols.createPacket(initiateConfigSync(player.connection))); + configSentToClient(player.connection); + } + + private void configSentToClient(ServerGamePacketListenerImpl handler) { + getHandshakeState(handler).state = HandshakeState.SENT; + + OptionalInt timeout = getSyncTimeout(config); + if (timeout.isEmpty()) { + return; + } + scheduledKicks.put(handler, new DelayedRunnable(timeout.getAsInt(), () -> { + if (getHandshakeState(handler).state != HandshakeState.ACCEPTED) { + LOGGER.warn( + "{} did not accept config syncing, config indicates we kick them.", + handler.getPlayer().getName().getString() + ); + handler.disconnect(SYNC_TIMEOUT_MESSAGE, PlayerKickEvent.Cause.PLUGIN); + } + })); + } + + private HandshakeState clientReplied(ServerGamePacketListenerImpl handler, ConfigResponseC2SPacket packet) { + var info = getHandshakeState(handler); + var player = handler.getPlayer(); + + if (info.state == HandshakeState.SENT) { + var protocolVersion = packet.protocolVersion(); + if (protocolVersion < 1 || protocolVersion > PROTOCOL_VERSION) { + LOGGER.warn( + "{} sent unknown protocol version, expected range 1-{}, got {}. Will attempt to proceed anyway.", + player.getName().getString(), + PROTOCOL_VERSION, + protocolVersion + ); + } + + if (protocolVersion == 2 && info.protocolVersion != 2) { + LOGGER.info("{} is using an older protocol version, resending.", player.getName().getString()); + info.state = HandshakeState.RESEND; + } else if (packet.success()) { + LOGGER.info("{} accepted server config.", player.getName().getString()); + info.state = HandshakeState.ACCEPTED; + } else { + LOGGER.warn( + "{} failed to process server config, check client logs find what went wrong.", + player.getName().getString()); + info.state = HandshakeState.FAILED; + } + info.protocolVersion = protocolVersion; + } + + return info.state; + } + + private boolean isLimited(ServerGamePacketListenerImpl net) { + return true; + // return net.getPlayer().getBukkitEntity().hasPermission(DoABarrelRoll.MODID + ".configure"); + } + + private ClientInfo getHandshakeState(ServerGamePacketListenerImpl handler) { + return syncStates.computeIfAbsent(handler, key -> new ClientInfo(HandshakeState.NOT_SENT, PROTOCOL_VERSION, true)); + } + + private ConfigSyncS2CPacket initiateConfigSync(ServerGamePacketListenerImpl handler) { + var isLimited = isLimited(handler); + getHandshakeState(handler).isLimited = isLimited; + return new ConfigSyncS2CPacket(PROTOCOL_VERSION, config, isLimited, isLimited ? DEFAULT : config); + } + + private static class ClientInfo { + private HandshakeState state; + private int protocolVersion; + private boolean isLimited; + + private ClientInfo(HandshakeState state, int protocolVersion, boolean isLimited) { + this.state = state; + this.protocolVersion = protocolVersion; + this.isLimited = isLimited; + } + } + + private static class DelayedRunnable { + private final Runnable runnable; + private final int delay; + private int ticks = 0; + + private DelayedRunnable(int delay, Runnable runnable) { + this.runnable = runnable; + this.delay = delay; + } + + private void tick() { + if (++ticks >= delay) { + runnable.run(); + } + } + + private boolean isDone() { + return ticks >= delay; + } + } + + private enum HandshakeState { + NOT_SENT, + SENT, + ACCEPTED, + FAILED, + RESEND + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java new file mode 100644 index 00000000..54ca72eb --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java @@ -0,0 +1,10 @@ +package org.dreeam.leaf.protocol; + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import org.jetbrains.annotations.NotNull; + +public interface LeafCustomPayload extends CustomPacketPayload { + @NotNull + @Override + Type type(); +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java new file mode 100644 index 00000000..ac069be5 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java @@ -0,0 +1,19 @@ +package org.dreeam.leaf.protocol; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +interface Protocol { + String namespace(); + List> c2s(); + List> s2c(); + void tickServer(MinecraftServer server); + void tickPlayer(ServerPlayer player); + void tickTracker(ServerPlayer player); + void disconnected(ServerPlayer conn); + void handle(ServerPlayer player, @NotNull LeafCustomPayload payload); +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java new file mode 100644 index 00000000..0af1e3d1 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java @@ -0,0 +1,97 @@ +package org.dreeam.leaf.protocol; + +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.DiscardedPayload; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Protocols { + private static final ObjectArrayList PROTOCOLS = new ObjectArrayList<>(); + + static void register(Protocol protocol) { + PROTOCOLS.add(protocol); + } + + static void unregister(Protocol protocol) { + PROTOCOLS.remove(protocol); + } + + public record TypeAndCodec(LeafCustomPayload.Type type, StreamCodec codec) {} + + public static void write(B byteBuf, LeafCustomPayload payload) { + for (Protocol protocol : PROTOCOLS) { + if (protocol.namespace().equals(payload.type().id().getNamespace())) { + encode(byteBuf, payload, protocol); + return; + } + } + } + + public static void handle(ServerPlayer player, @NotNull DiscardedPayload payload) { + for (Protocol protocol : PROTOCOLS) { + if (payload.type().id().getNamespace().equals(protocol.namespace())) { + var leafCustomPayload = decode(protocol, payload); + if (leafCustomPayload != null) { + protocol.handle(player, leafCustomPayload); + } + return; + } + } + } + + public static void tickServer(MinecraftServer server) { + for (Protocol protocol : PROTOCOLS) { + protocol.tickServer(server); + } + } + + public static void tickPlayer(ServerPlayer player) { + for (Protocol protocol : PROTOCOLS) { + protocol.tickPlayer(player); + } + } + + public static void tickTracker(ServerPlayer player) { + for (Protocol protocol : PROTOCOLS) { + protocol.tickTracker(player); + } + } + + public static void disconnected(ServerPlayer conn) { + for (Protocol protocol : PROTOCOLS) { + protocol.disconnected(conn); + } + } + + @Contract("_ -> new") + public static @NotNull ClientboundCustomPayloadPacket createPacket(LeafCustomPayload payload) { + return new ClientboundCustomPayloadPacket(payload); + } + + private static void encode(B byteBuf, LeafCustomPayload payload, Protocol protocol) { + for (var codec : protocol.s2c()) { + if (codec.type().id().equals(payload.type().id())) { + byteBuf.writeResourceLocation(payload.type().id()); + //noinspection unchecked,rawtypes + ((StreamCodec) codec.codec()).encode(byteBuf, payload); + return; + } + } + } + + private static @Nullable LeafCustomPayload decode(Protocol protocol, DiscardedPayload payload) { + for (var packet : protocol.c2s()) { + if (packet.type().id().equals(payload.type().id())) { + return packet.codec().decode(new FriendlyByteBuf(Unpooled.wrappedBuffer(payload.data()))); + } + } + return null; + } +} From a53ddc7f8cf51634c1e690bc4d286a7859891e60 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 7 May 2025 02:28:16 +0900 Subject: [PATCH 12/81] update async chunk send (#307) * cleanup * Async Chunk Send --------- Co-authored-by: Taiyou06 --- .../features/0132-Async-chunk-send.patch | 172 ++++++++++++++++++ .../features/0132-Async-chunk-sending.patch | 108 ----------- .../features/0036-async-chunk-send.patch | 19 ++ .../dreeam/leaf/async/AsyncChunkSending.java | 9 - .../leaf/async/chunk/AsyncChunkSend.java | 25 +++ .../async/chunk/AsyncChunkSendThread.java | 7 + .../config/modules/async/AsyncChunkSend.java | 7 + 7 files changed, 230 insertions(+), 117 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch delete mode 100644 leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch create mode 100644 leaf-server/paper-patches/features/0036-async-chunk-send.patch delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java diff --git a/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch b/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch new file mode 100644 index 00000000..196e225f --- /dev/null +++ b/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch @@ -0,0 +1,172 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 2 Mar 2025 21:23:20 +0100 +Subject: [PATCH] Async chunk send + + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +index a35e9fae8f8da0c42f0616c4f78dc396492673aa..2fef24acfaceab21aad6be50e6b29701fa460bfb 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +@@ -438,7 +438,15 @@ public final class RegionizedPlayerChunkLoader { + // Note: drop isAlive() check so that chunks properly unload client-side when the player dies + ((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager + .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player); +- this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ))); ++ // Leaf start - Async chunk send ++ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { ++ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute( ++ () -> this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ))) ++ ); ++ } else { ++ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ))); ++ } ++ // Leaf end - Async chunk send + // Paper start - PlayerChunkUnloadEvent + if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) { + new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent(); +diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +index 9e321ef1c3d5803519b243685f4ee598dc0cf640..641b9c8bcf92d01cf40df8fb6d658b9cec37c6bc 100644 +--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java ++++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +@@ -73,6 +73,44 @@ public class ClientboundLevelChunkPacketData { + this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entryx.getValue())); + } + } ++ // Leaf start - Async chunk send ++ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, BlockEntity[] blockEntities, CompoundTag heightmaps) { ++ this.heightmaps = heightmaps; ++ ++ if (Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread) { ++ var buffer = new io.netty.buffer.UnpooledByteBufAllocator(false).buffer(calculateChunkSize(levelChunk)); ++ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo); ++ var array = it.unimi.dsi.fastutil.bytes.ByteArrays.trim(buffer.array(), buffer.writerIndex()); ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setBuffer(array); ++ } ++ this.buffer = array; ++ } else { ++ this.buffer = new byte[calculateChunkSize(levelChunk)]; ++ // Paper start - Anti-Xray - Add chunk packet info ++ if (chunkPacketInfo != null) { ++ chunkPacketInfo.setBuffer(this.buffer); ++ } ++ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo); ++ } ++ ++ this.blockEntitiesData = Lists.newArrayList(); ++ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks ++ ++ for (BlockEntity blockEntity : blockEntities) { ++ // Paper start - Handle oversized block entities in chunks ++ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) { ++ net.minecraft.network.protocol.Packet packet = blockEntity.getUpdatePacket(); ++ if (packet != null) { ++ this.extraPackets.add(packet); ++ continue; ++ } ++ } ++ // Paper end - Handle oversized block entities in chunks ++ this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(blockEntity)); ++ } ++ } ++ // Leaf end - Async chunk send + + public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) { + this.heightmaps = buffer.readNbt(); +diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java +index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..6878cb15fdec7d2438e87ed7dc5fc50b2edbda9f 100644 +--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java ++++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java +@@ -44,6 +44,17 @@ public class ClientboundLevelChunkWithLightPacket implements Packet chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray ++ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo, blockEntities, heightmaps); // Paper - Anti-Xray ++ this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight); ++ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks ++ } ++ // Leaf end - Async chunk send + + private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) { + this.x = buffer.readInt(); +diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java +index 14878690a88fd4de3e2c127086607e6c819c636c..64a8b50bfac66f75d8c87d9e6e4000dc621437dd 100644 +--- a/net/minecraft/server/network/PlayerChunkSender.java ++++ b/net/minecraft/server/network/PlayerChunkSender.java +@@ -64,13 +64,29 @@ public class PlayerChunkSender { + if (!list.isEmpty()) { + ServerGamePacketListenerImpl serverGamePacketListenerImpl = player.connection; + this.unacknowledgedBatches++; +- serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE); ++ // Leaf start - Async chunk send ++ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { ++ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute( ++ () -> serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE) ++ ); ++ } else { ++ serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE); ++ } ++ // Leaf end - Async chunk send + + for (LevelChunk levelChunk : list) { + sendChunk(serverGamePacketListenerImpl, serverLevel, levelChunk); + } + +- serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size())); ++ // Leaf start - Async chunk send ++ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { ++ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute( ++ () -> serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size())) ++ ); ++ } else { ++ serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size())); ++ } ++ // Leaf end - Async chunk send + this.batchQuota = this.batchQuota - list.size(); + } + } +@@ -81,7 +97,23 @@ public class PlayerChunkSender { + // Paper start - Anti-Xray + public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { + final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk); +- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify)); ++ // Leaf start - Async chunk send ++ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { ++ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]); ++ var heightmaps = new net.minecraft.nbt.CompoundTag(); ++ ++ for (var entry : chunk.getHeightmaps()) { ++ if (entry.getKey().sendToClient()) { ++ heightmaps.put(entry.getKey().getSerializationKey(), new net.minecraft.nbt.LongArrayTag(entry.getValue().getRawData())); ++ } ++ } ++ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute( ++ () -> packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify, blockEntities, heightmaps)) ++ ); ++ } else { ++ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify)); ++ } ++ // Leaf end - Async chunk send + // Paper end - Anti-Xray + // Paper start - PlayerChunkLoadEvent + if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { +diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java +index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..e307e618775acb2052593e16d6ff2a5a9edbac4a 100644 +--- a/net/minecraft/world/level/chunk/LevelChunkSection.java ++++ b/net/minecraft/world/level/chunk/LevelChunkSection.java +@@ -18,7 +18,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ + public static final int SECTION_HEIGHT = 16; + public static final int SECTION_SIZE = 4096; + public static final int BIOME_CONTAINER_BITS = 2; +- short nonEmptyBlockCount; // Paper - package private ++ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - volatile + private short tickingBlockCount; + private short tickingFluidCount; + private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status diff --git a/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch b/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch deleted file mode 100644 index e8b716e1..00000000 --- a/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Sun, 2 Mar 2025 21:23:20 +0100 -Subject: [PATCH] Async chunk sending - - -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -index a35e9fae8f8da0c42f0616c4f78dc396492673aa..af49117695c0785033b984ff91550e2fccbbc5e6 100644 ---- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -@@ -411,19 +411,91 @@ public final class RegionizedPlayerChunkLoader { - this.delayedTicketOps.addLast(op); - } - -+ // Leaf start - Async chunk sending -+ /** -+ * Sends a chunk to the player. -+ * If async chunk sending is enabled, this will prepare and send the chunk packet asynchronously. -+ * Otherwise, it will use the synchronous chunk sending implementation. -+ */ - private void sendChunk(final int chunkX, final int chunkZ) { -- if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { -- ((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager -- .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$addReceivedChunk(this.player); -+ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ); - -- final LevelChunk chunk = ((ChunkSystemLevel)this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ); -+ if (!this.sentChunks.add(chunkKey)) { -+ // Already in our sent list - silently return instead of throwing an exception -+ return; -+ } -+ -+ // Get the chunk now, as we need it for both sync and async paths -+ final LevelChunk chunk = ((ChunkSystemLevel) this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ); -+ if (chunk == null) { -+ // Handle case where chunk is no longer loaded -+ this.sentChunks.remove(chunkKey); -+ return; -+ } -+ -+ // Try to mark the chunk as received by this player -+ try { -+ // This part needs to remain on the main thread as it affects shared state -+ ((ChunkSystemServerLevel) this.world).moonrise$getChunkTaskScheduler().chunkHolderManager -+ .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder.moonrise$addReceivedChunk(this.player); - -+ // Call onChunkWatch on the main thread as it might affect server state - PlatformHooks.get().onChunkWatch(this.world, chunk, this.player); -- PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk); -+ } catch (IllegalStateException e) { -+ // This happens if the chunk was already marked as received by this player -+ // Just remove it from our sent list and return -+ this.sentChunks.remove(chunkKey); - return; - } -- throw new IllegalStateException(); -+ -+ // Check if async chunk sending is enabled -+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { -+ // Async implementation -+ net.minecraft.Util.backgroundExecutor().execute(() -> { -+ try { -+ final net.minecraft.server.network.ServerGamePacketListenerImpl connection = this.player.connection; -+ final ServerLevel serverLevel = this.world; -+ -+ // Create the packet with anti-xray control flag -+ final net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket packet = new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( -+ chunk, serverLevel.getLightEngine(), null, null, -+ serverLevel.chunkPacketBlockController.shouldModify(this.player, chunk) -+ ); -+ -+ // Let the main thread handle the anti-xray processing -+ serverLevel.getServer().execute(() -> { -+ if (this.removed || !this.sentChunks.contains(chunkKey)) { -+ return; -+ } -+ -+ // This will trigger anti-xray processing and mark the packet as ready when done -+ // The packet automatically handles readiness -+ // Send the packet (which will be held until ready by the network layer) -+ connection.send(packet); -+ -+ // Fire events and send POI packets -+ if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ new io.papermc.paper.event.packet.PlayerChunkLoadEvent( -+ new org.bukkit.craftbukkit.CraftChunk(chunk), -+ this.player.getBukkitEntity() -+ ).callEvent(); -+ } -+ -+ net.minecraft.network.protocol.game.DebugPackets.sendPoiPacketsForChunk(serverLevel, chunk.getPos()); -+ }); -+ } catch (Exception e) { -+ org.dreeam.leaf.async.AsyncChunkSending.LOGGER.error("Failed to send chunk asynchronously!", e); -+ -+ if (!this.removed) { -+ this.sentChunks.remove(chunkKey); -+ } -+ } -+ }); -+ } else { -+ PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk); -+ } - } -+ // Leaf end - Async chunk sending - - private void sendUnloadChunk(final int chunkX, final int chunkZ) { - if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { diff --git a/leaf-server/paper-patches/features/0036-async-chunk-send.patch b/leaf-server/paper-patches/features/0036-async-chunk-send.patch new file mode 100644 index 00000000..a0008705 --- /dev/null +++ b/leaf-server/paper-patches/features/0036-async-chunk-send.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 2 May 2025 18:22:24 -0700 +Subject: [PATCH] async chunk send + + +diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java +index ee2d3a54d760f9c26542eab03c51651a30e279a0..0a4382315ee62e8defa50b3e8018a8f97687c139 100644 +--- a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java ++++ b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java +@@ -185,7 +185,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo + return; + } + +- if (!Bukkit.isPrimaryThread()) { ++ if (!Bukkit.isPrimaryThread() && !(Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread)) { // Leaf - Async chunk send + // Plugins? + MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo)); + return; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java deleted file mode 100644 index a54cefb3..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.dreeam.leaf.async; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class AsyncChunkSending { - - public static final Logger LOGGER = LogManager.getLogger(AsyncChunkSending.class.getSimpleName()); -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java new file mode 100644 index 00000000..d64f225d --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java @@ -0,0 +1,25 @@ +package org.dreeam.leaf.async.chunk; + +import net.minecraft.Util; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class AsyncChunkSend { + public static final ExecutorService POOL = new ThreadPoolExecutor( + 1, 1, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new com.google.common.util.concurrent.ThreadFactoryBuilder() + .setPriority(Thread.NORM_PRIORITY - 2) + .setNameFormat("Leaf Async Chunk Send Thread") + .setUncaughtExceptionHandler(Util::onThreadException) + .setThreadFactory(AsyncChunkSendThread::new) + .build(), + new ThreadPoolExecutor.DiscardPolicy() + ); + public static final Logger LOGGER = LogManager.getLogger("Leaf Async Chunk Send"); +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java new file mode 100644 index 00000000..7e9f8bcb --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java @@ -0,0 +1,7 @@ +package org.dreeam.leaf.async.chunk; + +public class AsyncChunkSendThread extends Thread { + protected AsyncChunkSendThread(Runnable task) { + super(task); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java index 89ef74ed..5313cb62 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncChunkSend.java @@ -10,6 +10,7 @@ public class AsyncChunkSend extends ConfigModules { } public static boolean enabled = false; + private static boolean asyncChunkSendInitialized; @Override public void onLoaded() { @@ -20,6 +21,12 @@ public class AsyncChunkSend extends ConfigModules { 使区块数据包准备和发送异步化以提高服务器性能. 当许多玩家同时加载区块时, 这可以显著减少主线程负载."""); + if (asyncChunkSendInitialized) { + config.getConfigSection(getBasePath()); + return; + } + asyncChunkSendInitialized = true; + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); } } From f45af32322f2d587736e9c745fbaea615b4868e4 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 00:15:49 +0200 Subject: [PATCH 13/81] fix max mspt spikes caused by world save --- .../0166-Save-world-async-properly.patch | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch diff --git a/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch b/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch new file mode 100644 index 00000000..e76e1021 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 00:05:01 +0200 +Subject: [PATCH] Save world async properly + +P.S from Tai: I've been using this fix for weeks in my own server but didn't had balls to push it as thought it may cause issues but, it's merged in paper 1.21.5 now. + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index ba1dd51e7187a80e8438e46383257c22f5382130..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1439,7 +1439,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + if (doFull) { +- this.saveLevelData(true); ++ this.saveLevelData(false); + } + // chunk autosave is already called by the ChunkSystem during unload processing (ChunkMap#processUnloads) + // Copied from save() From 8c0e3772fa4db4b81edc3efb3ff030160c1ddc89 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 8 May 2025 16:35:21 +0900 Subject: [PATCH 14/81] synchronize while iterator attributesToSync --- .../features/0085-Multithreaded-Tracker.patch | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index 337ace2e..1cad3f71 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -177,7 +177,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index d8298c7925e3bcea07ead4d438478cc51abcfa16..a13ce614722d26dcbb2f7f3ceeb68915bbaec304 100644 +index d8298c7925e3bcea07ead4d438478cc51abcfa16..38314fcb660f6cbb36d60434d24df5b579425eb3 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -110,8 +110,16 @@ public class ServerEntity { @@ -199,28 +199,37 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..a13ce614722d26dcbb2f7f3ceeb68915 } } ); -@@ -434,15 +442,17 @@ public class ServerEntity { +@@ -434,6 +442,21 @@ public class ServerEntity { if (this.entity instanceof LivingEntity) { Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); -- if (!attributesToSync.isEmpty()) { -+ // Leaf start - petal - Multithreaded tracker - send in the main thread -+ var clonedAttributes = new it.unimi.dsi.fastutil.objects.ObjectArraySet<>(attributesToSync); -+ if (!clonedAttributes.isEmpty()) { ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ synchronized (attributesToSync) { ++ if (!attributesToSync.isEmpty()) { ++ // CraftBukkit start - Send scaled max health ++ if (this.entity instanceof ServerPlayer serverPlayer) { ++ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); ++ } ++ // CraftBukkit end ++ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); ++ } ++ ++ attributesToSync.clear(); ++ } ++ } else { + if (!attributesToSync.isEmpty()) { // CraftBukkit start - Send scaled max health if (this.entity instanceof ServerPlayer serverPlayer) { -- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(clonedAttributes, false); - } - // CraftBukkit end -- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); -+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), clonedAttributes)); // Leaf - petal - Multithreaded tracker - send in the main thread +@@ -444,6 +467,8 @@ public class ServerEntity { } -- -+ // Leaf end - petal - Multithreaded tracker - send in the main thread + attributesToSync.clear(); ++ } ++ // Leaf end } } + diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index d6ebc25dc5f04194edde5ad3a1166113e5542a1d..49cbdf014d0626b36eb4c451b6de09508822b7fd 100644 --- a/net/minecraft/server/level/ServerLevel.java From 152fbed3d1a4e6c1cd83167cdd15b75b8f936752 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 8 May 2025 17:03:07 +0900 Subject: [PATCH 15/81] Don't parallel tick the tracker of entity --- .../features/0085-Multithreaded-Tracker.patch | 24 +++++++++++++------ .../async/tracker/MultithreadedTracker.java | 5 +++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index 1cad3f71..cc7e766e 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -37,7 +37,7 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10f9faa6b2 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e92867b682 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -70,7 +70,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,7 +1089,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,12 +1089,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; @@ -79,7 +79,14 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; -@@ -1100,7 +1116,39 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + private ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk; + ++ public final Object sync = new Object(); // Leaf ++ + @Override + public final void moonrise$tick(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { + if (chunk == null) { +@@ -1100,8 +1118,41 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -95,6 +102,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 + this.updatePlayer(player); + } +- for (int i = 0, len = players.size(); i < len; ++i) { + if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { + // need to purge any players possible not in the chunk list + for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { @@ -116,10 +124,12 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 + updatePlayerTasks.run(); + } + } else { - for (int i = 0, len = players.size(); i < len; ++i) { ++ final int playersLength = Math.min(playersRaw.length, players.size()); ++ for (int i = 0; i < playersLength; ++i) { final ServerPlayer player = playersRaw[i]; this.updatePlayer(player); -@@ -1115,6 +1163,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } +@@ -1115,6 +1166,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } } @@ -128,7 +138,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 } @Override -@@ -1176,7 +1226,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1176,7 +1229,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -137,7 +147,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..8986c059e7aadb58ae8d9ab7b848de10 serverPlayerConnection.send(packet); } } -@@ -1189,21 +1239,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1242,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index 91b48ee0..7880c0a8 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -67,7 +67,10 @@ public class MultithreadedTracker { if (tracker == null) continue; - tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + // Don't Parallel Tick Tracker of Entity + synchronized (tracker.sync) { + tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + } tracker.serverEntity.sendChanges(); } }); From 2d2eda80694613d5735b819035a4885148d2ab51 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 8 May 2025 17:14:33 +0900 Subject: [PATCH 16/81] defer init tracker thread pool --- .../async/tracker/MultithreadedTracker.java | 30 ++++++++++++------- .../modules/async/MultithreadedTracker.java | 3 ++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index 7880c0a8..d5f6a6e8 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -23,20 +23,28 @@ public class MultithreadedTracker { private static final String THREAD_PREFIX = "Leaf Async Tracker"; private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX); private static long lastWarnMillis = System.currentTimeMillis(); - private static final ThreadPoolExecutor trackerExecutor = new ThreadPoolExecutor( - getCorePoolSize(), - getMaxPoolSize(), - getKeepAliveTime(), TimeUnit.SECONDS, - getQueueImpl(), - getThreadFactory(), - getRejectedPolicy() - ); + private static ThreadPoolExecutor TRACKER_EXECUTOR = null; private MultithreadedTracker() { } + public static void init() { + if (TRACKER_EXECUTOR == null) { + TRACKER_EXECUTOR = new ThreadPoolExecutor( + getCorePoolSize(), + getMaxPoolSize(), + getKeepAliveTime(), TimeUnit.SECONDS, + getQueueImpl(), + getThreadFactory(), + getRejectedPolicy() + ); + } else { + throw new IllegalStateException(); + } + } + public static Executor getTrackerExecutor() { - return trackerExecutor; + return TRACKER_EXECUTOR; } public static void tick(ChunkSystemServerLevel level) { @@ -59,7 +67,7 @@ public class MultithreadedTracker { final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); // Move tracking to off-main - trackerExecutor.execute(() -> { + TRACKER_EXECUTOR.execute(() -> { for (final Entity entity : trackerEntitiesRaw) { if (entity == null) continue; @@ -97,7 +105,7 @@ public class MultithreadedTracker { } // batch submit tasks - trackerExecutor.execute(() -> { + TRACKER_EXECUTOR.execute(() -> { for (final Runnable sendChanges : sendChangesTasks) { if (sendChanges == null) continue; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java index a6b42058..7a01b4b1 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java @@ -57,5 +57,8 @@ public class MultithreadedTracker extends ConfigModules { if (asyncEntityTrackerQueueSize <= 0) asyncEntityTrackerQueueSize = asyncEntityTrackerMaxThreads * 384; + if (enabled) { + org.dreeam.leaf.async.tracker.MultithreadedTracker.init(); + } } } From 56cc95b9eea8c7b45ab577e896872968b34f5f15 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 10:46:59 +0200 Subject: [PATCH 17/81] Reduce ChunkSource Updates --- .../0166-Save-world-async-properly.patch | 1 - .../0167-reduce-PlayerChunk-Updates.patch | 32 +++++++++++++++++++ .../modules/opt/ReduceChunkSourceUpdates.java | 23 +++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java diff --git a/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch b/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch index e76e1021..198d4cdd 100644 --- a/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch +++ b/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch @@ -5,7 +5,6 @@ Subject: [PATCH] Save world async properly P.S from Tai: I've been using this fix for weeks in my own server but didn't had balls to push it as thought it may cause issues but, it's merged in paper 1.21.5 now. - diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index ba1dd51e7187a80e8438e46383257c22f5382130..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 --- a/net/minecraft/server/level/ServerLevel.java diff --git a/leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch new file mode 100644 index 00000000..60b0456b --- /dev/null +++ b/leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 10:08:54 +0200 +Subject: [PATCH] reduce PlayerChunk Updates + + +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index abe79d07196de0a10a382d4c37161c7eb4a604ae..405b8da8b886b5caac7ed774472e106374c42185 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1367,6 +1367,10 @@ public class ServerGamePacketListenerImpl + this.resetPosition(); + } + ++ // Leaf start - ReduceChunkSourceUpdates ++ final net.minecraft.world.level.ChunkPos playerStartChunkPosition = this.player.chunkPosition(); ++ // Leaf end - ReduceChunkSourceUpdates ++ + if (!this.updateAwaitingTeleport() && this.player.hasClientLoaded()) { + double d = clampHorizontal(packet.getX(this.player.getX())); final double toX = d; // Paper - OBFHELPER + double d1 = clampVertical(packet.getY(this.player.getY())); final double toY = d1; // Paper - OBFHELPER +@@ -1638,7 +1642,9 @@ public class ServerGamePacketListenerImpl + && !isFallFlying + && !isAutoSpinAttack + && this.noBlocksAround(this.player); +- this.player.serverLevel().getChunkSource().move(this.player); ++ if (!org.dreeam.leaf.config.modules.opt.ReduceChunkSourceUpdates.enabled || this.player.serverLevel() != serverLevel || this.player.chunkPosition() == playerStartChunkPosition) { ++ this.player.serverLevel().getChunkSource().move(this.player); ++ } + Vec3 vec3 = new Vec3(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z); + this.player.setOnGroundWithMovement(packet.isOnGround(), packet.horizontalCollision(), vec3); + this.player.doCheckFallDamage(vec3.x, vec3.y, vec3.z, packet.isOnGround()); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java new file mode 100644 index 00000000..e89a0ad2 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java @@ -0,0 +1,23 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class ReduceChunkSourceUpdates extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".reduce-chunk-source-updates"; + } + + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".enabled", enabled, + config.pickStringRegionBased( + "Reduces chunk source updates on inter-chunk player moves. (Recommended to enable)", + "减少玩家跨区块移动时的区块源更新。" + ) + ); + } +} From f0ff93a1ce46992895c10e760c68176966f647ea Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 11:17:05 +0200 Subject: [PATCH 18/81] AlternativeJoin to skip sync in syncAfterConfigurationChange --- .../0168-Alternative-join-logic.patch | 86 +++++++++++++++++++ .../modules/network/AlternativeJoin.java | 20 +++++ 2 files changed, 106 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java diff --git a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch new file mode 100644 index 00000000..529b0a20 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 11:14:06 +0200 +Subject: [PATCH] Alternative join logic + + +diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e059737086527a7e 100644 +--- a/net/minecraft/network/Connection.java ++++ b/net/minecraft/network/Connection.java +@@ -66,6 +66,8 @@ import org.slf4j.Logger; + import org.slf4j.Marker; + import org.slf4j.MarkerFactory; + ++import static org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin; ++ + public class Connection extends SimpleChannelInboundHandler> { + private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F; + private static final Logger LOGGER = LogUtils.getLogger(); +@@ -340,36 +342,43 @@ public class Connection extends SimpleChannelInboundHandler> { + public void setupInboundProtocol(ProtocolInfo protocolInfo, T packetInfo) { + this.validateListener(protocolInfo, packetInfo); + if (protocolInfo.flow() != this.getReceiving()) { +- throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); ++ throw new IllegalStateException("Invalid inbound protocol: " + String.valueOf(protocolInfo.id())); ++ } ++ this.packetListener = packetInfo; ++ this.disconnectListener = null; ++ UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); ++ BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); ++ if (bundlerInfo != null) { ++ PacketBundlePacker packetBundlePacker = new PacketBundlePacker(bundlerInfo); ++ inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", (ChannelHandler)packetBundlePacker)); ++ } ++ if (!AlternativeJoin) { ++ Connection.syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); + } else { +- this.packetListener = packetInfo; +- this.disconnectListener = null; +- UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); +- BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); +- if (bundlerInfo != null) { +- PacketBundlePacker packetBundlePacker = new PacketBundlePacker(bundlerInfo); +- inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", packetBundlePacker)); +- } +- +- syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); ++ this.channel.writeAndFlush(inboundConfigurationTask); + } + } + + public void setupOutboundProtocol(ProtocolInfo protocolInfo) { ++ boolean flag; + if (protocolInfo.flow() != this.getSending()) { +- throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); ++ throw new IllegalStateException("Invalid outbound protocol: " + String.valueOf(protocolInfo.id())); ++ } ++ UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); ++ BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); ++ if (bundlerInfo != null) { ++ PacketBundleUnpacker packetBundleUnpacker = new PacketBundleUnpacker(bundlerInfo); ++ outboundConfigurationTask = outboundConfigurationTask.andThen(context -> context.pipeline().addAfter("encoder", "unbundler", (ChannelHandler)packetBundleUnpacker)); ++ } ++ boolean bl = flag = protocolInfo.id() == ConnectionProtocol.LOGIN; ++ if (!AlternativeJoin) { ++ Connection.syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { ++ this.sendLoginDisconnect = flag; ++ }))); + } else { +- UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); +- BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); +- if (bundlerInfo != null) { +- PacketBundleUnpacker packetBundleUnpacker = new PacketBundleUnpacker(bundlerInfo); +- outboundConfigurationTask = outboundConfigurationTask.andThen( +- context -> context.pipeline().addAfter("encoder", "unbundler", packetBundleUnpacker) +- ); +- } +- +- boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; +- syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); ++ this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { ++ this.sendLoginDisconnect = flag; ++ })); + } + } + diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java new file mode 100644 index 00000000..b9f86891 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java @@ -0,0 +1,20 @@ +package org.dreeam.leaf.config.modules.network; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class AlternativeJoin extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.NETWORK.getBaseKeyName(); + } + + public static boolean AlternativeJoin = true; + + @Override + public void onLoaded() { + AlternativeJoin = config.getBoolean(getBasePath() + ".alternative-join", AlternativeJoin, config.pickStringRegionBased( + "Use alternative login logic to skip synchronization.", + "使用替代登录逻辑以跳过同步。")); + } +} From 0e3028fceb978b681278ac8922f3bb92da8a2b01 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 8 May 2025 18:35:01 +0900 Subject: [PATCH 19/81] minecart_improvements Fix minecart tracker data race --- .../features/0085-Multithreaded-Tracker.patch | 26 +++++++++++++++++++ .../async/tracker/MultithreadedTracker.java | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index cc7e766e..a82a8d0e 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -367,6 +367,32 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..d6e7685cb0e9beaa017bcc665f0f1c7c } private void onAttributeModified(AttributeInstance instance) { +diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java +index 724466d14c925704671e510cea1919ee95a2ae02..4426b344677ab9f2753dd2d219921bcb7cf39980 100644 +--- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java ++++ b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java +@@ -35,13 +35,20 @@ public class NewMinecartBehavior extends MinecartBehavior { + private int cachedLerpDelay; + private float cachedPartialTick; + private int lerpDelay = 0; +- public final List lerpSteps = new LinkedList<>(); ++ public final List lerpSteps; // Leaf - Multithreaded tracker + public final List currentLerpSteps = new LinkedList<>(); + public double currentLerpStepsTotalWeight = 0.0; + public NewMinecartBehavior.MinecartStep oldLerp = NewMinecartBehavior.MinecartStep.ZERO; + + public NewMinecartBehavior(AbstractMinecart minecart) { + super(minecart); ++ // Leaf start - Multithreaded tracker ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ lerpSteps = it.unimi.dsi.fastutil.objects.ObjectLists.synchronize(new it.unimi.dsi.fastutil.objects.ObjectArrayList<>()); ++ } else { ++ lerpSteps = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ } ++ // Leaf end - Multithreaded tracker + } + + @Override diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java index 681dec447486138088fe5f705ef4fadab531139f..27f8a22d798a17dbd5949d1b6ff0526837fe91d5 100644 --- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index d5f6a6e8..ba41ecdc 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -78,8 +78,8 @@ public class MultithreadedTracker { // Don't Parallel Tick Tracker of Entity synchronized (tracker.sync) { tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + tracker.serverEntity.sendChanges(); } - tracker.serverEntity.sendChanges(); } }); } From dfcadc04c40879e2f95ba02de361f18fd6a0e5fb Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 11:56:51 +0200 Subject: [PATCH 20/81] cleanup and a fix to AltJoin --- .../0168-Alternative-join-logic.patch | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch index 529b0a20..8783f0f7 100644 --- a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch +++ b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Alternative join logic diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e059737086527a7e 100644 +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..1eba5608af0bf70d6c7a9e34c5726ac6eaa6e096 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -66,6 +66,8 @@ import org.slf4j.Logger; @@ -17,12 +17,10 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 public class Connection extends SimpleChannelInboundHandler> { private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F; private static final Logger LOGGER = LogUtils.getLogger(); -@@ -340,36 +342,43 @@ public class Connection extends SimpleChannelInboundHandler> { - public void setupInboundProtocol(ProtocolInfo protocolInfo, T packetInfo) { +@@ -341,35 +343,54 @@ public class Connection extends SimpleChannelInboundHandler> { this.validateListener(protocolInfo, packetInfo); if (protocolInfo.flow() != this.getReceiving()) { -- throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); -+ throw new IllegalStateException("Invalid inbound protocol: " + String.valueOf(protocolInfo.id())); + throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); + } + this.packetListener = packetInfo; + this.disconnectListener = null; @@ -32,8 +30,9 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 + PacketBundlePacker packetBundlePacker = new PacketBundlePacker(bundlerInfo); + inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", (ChannelHandler)packetBundlePacker)); + } ++ ChannelFuture future = this.channel.writeAndFlush(inboundConfigurationTask); + if (!AlternativeJoin) { -+ Connection.syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); ++ Connection.syncAfterConfigurationChange(future); } else { - this.packetListener = packetInfo; - this.disconnectListener = null; @@ -45,7 +44,13 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 - } - - syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); -+ this.channel.writeAndFlush(inboundConfigurationTask); ++ java.util.concurrent.CompletableFuture.runAsync(() -> { ++ try { ++ future.await(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } ++ }); } } @@ -53,7 +58,7 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 + boolean flag; if (protocolInfo.flow() != this.getSending()) { - throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); -+ throw new IllegalStateException("Invalid outbound protocol: " + String.valueOf(protocolInfo.id())); ++ throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); + } + UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); + BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); @@ -62,10 +67,11 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 + outboundConfigurationTask = outboundConfigurationTask.andThen(context -> context.pipeline().addAfter("encoder", "unbundler", (ChannelHandler)packetBundleUnpacker)); + } + boolean bl = flag = protocolInfo.id() == ConnectionProtocol.LOGIN; ++ ChannelFuture future = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { ++ this.sendLoginDisconnect = flag; ++ })); + if (!AlternativeJoin) { -+ Connection.syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { -+ this.sendLoginDisconnect = flag; -+ }))); ++ Connection.syncAfterConfigurationChange(future); } else { - UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); - BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); @@ -78,9 +84,13 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..85b3e78ae72d550c5cce0b46e0597370 - - boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; - syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); -+ this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { -+ this.sendLoginDisconnect = flag; -+ })); ++ java.util.concurrent.CompletableFuture.runAsync(() -> { ++ try { ++ future.await(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } ++ }); } } From e5bacbcd93609e55dc01d5bbaa81a369ba730e47 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 12:09:05 +0200 Subject: [PATCH 21/81] ok it's done --- .../features/0168-Alternative-join-logic.patch | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch index 8783f0f7..e6c1b508 100644 --- a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch +++ b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Alternative join logic diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..1eba5608af0bf70d6c7a9e34c5726ac6eaa6e096 100644 +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..a0df7cb76b5877a0fcfcac457c5031502ecd18f5 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -66,6 +66,8 @@ import org.slf4j.Logger; @@ -57,8 +57,7 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..1eba5608af0bf70d6c7a9e34c5726ac6 public void setupOutboundProtocol(ProtocolInfo protocolInfo) { + boolean flag; if (protocolInfo.flow() != this.getSending()) { -- throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); -+ throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); + throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); + } + UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); + BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); From 3821c552e547241f4e3c2956d95f815c391e5c2a Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 13:34:09 +0200 Subject: [PATCH 22/81] Optimise BlockEntity tickersInLevel --- ...ptimise-BlockEntities-tickersInLevel.patch | 19 +++++++++++++++++++ .../modules/opt/OptimiseBlockEntities.java | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java diff --git a/leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch new file mode 100644 index 00000000..0f09babb --- /dev/null +++ b/leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 13:30:07 +0200 +Subject: [PATCH] Optimise BlockEntities tickersInLevel + + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 546fb78339c005ed71142cb3c894f816b8c72d08..22ccc6bc8444f2dba1db8a0e06d5018b6631c442 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -72,7 +72,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + return ""; + } + }; +- private final Map tickersInLevel = Maps.newHashMap(); ++ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimiseBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); + public boolean loaded; + public final ServerLevel level; // CraftBukkit - type + @Nullable diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java new file mode 100644 index 00000000..e1ddf5eb --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java @@ -0,0 +1,18 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimiseBlockEntities extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".optimise-block-entities", enabled); + } +} From cbd9c506479d273e08f9a83783cd86033ac9de66 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 19:50:25 +0200 Subject: [PATCH 23/81] [ci skip] cleanup --- .../features/0168-Alternative-join-logic.patch | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch index e6c1b508..36d4f085 100644 --- a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch +++ b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch @@ -5,19 +5,10 @@ Subject: [PATCH] Alternative join logic diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..a0df7cb76b5877a0fcfcac457c5031502ecd18f5 100644 +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..7423729fc9a6501c3a6408442ce28066067ce865 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -66,6 +66,8 @@ import org.slf4j.Logger; - import org.slf4j.Marker; - import org.slf4j.MarkerFactory; - -+import static org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin; -+ - public class Connection extends SimpleChannelInboundHandler> { - private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F; - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -341,35 +343,54 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -341,35 +341,54 @@ public class Connection extends SimpleChannelInboundHandler> { this.validateListener(protocolInfo, packetInfo); if (protocolInfo.flow() != this.getReceiving()) { throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); @@ -31,7 +22,7 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..a0df7cb76b5877a0fcfcac457c503150 + inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", (ChannelHandler)packetBundlePacker)); + } + ChannelFuture future = this.channel.writeAndFlush(inboundConfigurationTask); -+ if (!AlternativeJoin) { ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin) { + Connection.syncAfterConfigurationChange(future); } else { - this.packetListener = packetInfo; @@ -69,7 +60,7 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..a0df7cb76b5877a0fcfcac457c503150 + ChannelFuture future = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { + this.sendLoginDisconnect = flag; + })); -+ if (!AlternativeJoin) { ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin) { + Connection.syncAfterConfigurationChange(future); } else { - UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); From f4d9c7a5122ef702d6ad35d5ea0750f1cb640786 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 21:56:32 +0200 Subject: [PATCH 24/81] Pluto: check if cactus can survive being placed --- ...he-cactus-can-even-survive-being-pla.patch | 70 +++++++++++++++++++ .../opt/CheckSurvivalBeforeGrowth.java | 22 ++++++ 2 files changed, 92 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/CheckSurvivalBeforeGrowth.java diff --git a/leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch new file mode 100644 index 00000000..a985202f --- /dev/null +++ b/leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch @@ -0,0 +1,70 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 21:24:48 +0200 +Subject: [PATCH] Pluto: Check if the cactus can even survive being placed + + +diff --git a/net/minecraft/world/level/block/CactusBlock.java b/net/minecraft/world/level/block/CactusBlock.java +index 079b4c95cf81119ca99daeb159aefca389afed74..8fe29455d7ae44f43c663718d38ea2d8cf639797 100644 +--- a/net/minecraft/world/level/block/CactusBlock.java ++++ b/net/minecraft/world/level/block/CactusBlock.java +@@ -49,10 +49,15 @@ public class CactusBlock extends Block implements BonemealableBlock { // Purpur + @Override + protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + BlockPos blockPos = pos.above(); +- if (level.isEmptyBlock(blockPos)) { ++ // Pluto start - Decrease chunk/block lookups ++ net.minecraft.world.level.chunk.LevelChunk chunk = level.getChunkIfLoaded(blockPos); ++ if (chunk == null) return; ++ ++ if (chunk.getBlockState(blockPos).isAir()) { ++ // Pluto end - Decrease chunk/block lookups + int i = 1; + +- while (level.getBlockState(pos.below(i)).is(this)) { ++ while (chunk.getBlockState(pos.below(i)).is(this)) { // Pluto - Decrease chunk/block lookups + i++; + } + +@@ -61,11 +66,28 @@ public class CactusBlock extends Block implements BonemealableBlock { // Purpur + + int modifier = level.spigotConfig.cactusModifier; // Spigot - SPIGOT-7159: Better modifier resolution + if (ageValue >= 15 || (modifier != 100 && random.nextFloat() < (modifier / (100.0f * 16)))) { // Spigot - SPIGOT-7159: Better modifier ++ // Pluto start - Check if the cactus can even survive being placed ++ if (org.dreeam.leaf.config.modules.opt.CheckSurvivalBeforeGrowth.cactusCheckSurvivalBeforeGrowth && !canSurvive(level, blockPos)) { ++ level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos, Block.getId(state)); ++ // We're going to fake the block breaking to match vanilla standards. ++ for (net.minecraft.world.item.ItemStack drop : Block.getDrops(state, level, pos, null)) { // Use base cactus since we don't place a block ++ Block.popResource(level, blockPos, drop); ++ } ++ level.setBlock(pos, state.setValue(CactusBlock.AGE, 0), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); ++ return; ++ } ++ // Pluto end - Check if the cactus can even survive being placed + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockPos, this.defaultBlockState()); // CraftBukkit + BlockState blockState = state.setValue(AGE, Integer.valueOf(0)); + level.setBlock(pos, blockState, 4); + level.neighborChanged(blockState, blockPos, this, null, false); + } else if (modifier == 100 || random.nextFloat() < (modifier / (100.0f * 16))) { // Spigot - SPIGOT-7159: Better modifier resolution ++ // Pluto start - Check if the cactus can even survive being placed ++ if (org.dreeam.leaf.config.modules.opt.CheckSurvivalBeforeGrowth.cactusCheckSurvivalBeforeGrowth) { ++ level.setBlock(pos, state.setValue(CactusBlock.AGE, ageValue + 1), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); ++ return; ++ } ++ // Pluto end - Check if the cactus can even survive being placed + level.setBlock(pos, state.setValue(AGE, Integer.valueOf(ageValue + 1)), 4); + } + } +@@ -102,6 +124,12 @@ public class CactusBlock extends Block implements BonemealableBlock { // Purpur + + @Override + protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { ++ // Pluto start - Check if the cactus can even survive being placed ++ return canSurvive(level, pos); ++ } ++ ++ protected boolean canSurvive(LevelReader level, BlockPos pos) { ++ // Pluto end - Check if the cactus can even survive being placed + for (Direction direction : Direction.Plane.HORIZONTAL) { + BlockState blockState = level.getBlockState(pos.relative(direction)); + if ((level.getWorldBorder().world.purpurConfig.cactusBreaksFromSolidNeighbors && blockState.isSolid()) || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) { // Purpur - Cactus breaks from solid neighbors config diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/CheckSurvivalBeforeGrowth.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/CheckSurvivalBeforeGrowth.java new file mode 100644 index 00000000..0058b1b8 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/CheckSurvivalBeforeGrowth.java @@ -0,0 +1,22 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class CheckSurvivalBeforeGrowth extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".check-survival-before-growth"; + } + + public static boolean cactusCheckSurvivalBeforeGrowth = false; + + @Override + public void onLoaded() { + cactusCheckSurvivalBeforeGrowth = config.getBoolean(getBasePath() + ".cactus-check-survival", cactusCheckSurvivalBeforeGrowth, + config.pickStringRegionBased(""" + Check if a cactus can survive before growing.""", + """ + 在仙人掌生长前检查其是否能够存活。""")); + } +} From 49cc2a0f929cadcffe5fc8f4a85b4888411bdb2b Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 23:16:55 +0200 Subject: [PATCH 25/81] park less --- .../main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index 9a656f4d..d01e365b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -65,7 +65,7 @@ public class AsyncGoalExecutor { submit(id); } } - if ((tickCount & 7L) == 7L) { + if ((tickCount & 10L) == 10L) { unpark(); } tickCount += 1; From 08287d95dbd2d35a3e1f790a52e27bee32d832a1 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 9 May 2025 11:28:49 +0900 Subject: [PATCH 26/81] threshold config --- .../java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java | 2 +- .../leaf/config/modules/async/AsyncTargetFinding.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index d01e365b..b02ad4ab 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -65,7 +65,7 @@ public class AsyncGoalExecutor { submit(id); } } - if ((tickCount & 10L) == 10L) { + if ((tickCount % AsyncTargetFinding.threshold) == 0L) { unpark(); } tickCount += 1; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index 478f132c..adf14fd7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -16,6 +16,7 @@ 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,11 +37,15 @@ public class AsyncTargetFinding extends ConfigModules { 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", 4096); + 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; From b63f850b91a8071bcbcd597f6f92c879cefdb9fe Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 9 May 2025 11:57:49 +0900 Subject: [PATCH 27/81] move and flush position while knockback player (#316) * move and flush position while knockback * fix compile error * rename --------- Co-authored-by: Taiyou06 --- ...e-and-flush-location-while-knockback.patch | 36 +++++++++++++++++++ .../config/modules/gameplay/Knockback.java | 4 +++ 2 files changed, 40 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch new file mode 100644 index 00000000..1dfa2928 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Thu, 8 May 2025 04:56:30 +0900 +Subject: [PATCH] move and flush location while knockback + + +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 477455fdfcc591a89823e88983eb12dabb078d9b..ffff3c710f6a96de9372fe07ffa69e65d392273f 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1415,6 +1415,25 @@ public abstract class Player extends LivingEntity { + ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); + target.hurtMarked = false; + target.setDeltaMovement(deltaMovement); ++ ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { ++ ServerPlayer targetPlayer = (ServerPlayer) target; ++ Vec3 before = targetPlayer.getDeltaMovement(); ++ targetPlayer.aiStep(); ++ targetPlayer.setDeltaMovement(before); ++ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); ++ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ targetPlayer.connection.resumeFlushing(); ++ targetPlayer.hasImpulse = true; ++ if (this instanceof ServerPlayer player1) { ++ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); ++ player1.connection.resumeFlushing(); ++ player1.hasImpulse = true; ++ } ++ } ++ // Leaf end + } + // CraftBukkit end + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java index 916500e7..b0610c61 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java @@ -2,6 +2,7 @@ package org.dreeam.leaf.config.modules.gameplay; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; public class Knockback extends ConfigModules { @@ -12,6 +13,8 @@ public class Knockback extends ConfigModules { public static boolean snowballCanKnockback = false; public static boolean eggCanKnockback = false; public static boolean canPlayerKnockbackZombie = true; + @Experimental + public static boolean flushKnockback = false; @Override public void onLoaded() { @@ -30,5 +33,6 @@ public class Knockback extends ConfigModules { "Make players can knockback zombie.", "使玩家可以击退僵尸." )); + flushKnockback = config.getBoolean(getBasePath() + ".flush-location-while-knockback-player", flushKnockback); } } From cf6c916f2482fd164563affae6686cfd07bc0eac Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 9 May 2025 16:56:37 +0900 Subject: [PATCH 28/81] async switch connection state --- .../0168-Alternative-join-logic.patch | 86 ------------ .../0168-async-switch-connection-state.patch | 129 ++++++++++++++++++ .../modules/network/AlternativeJoin.java | 8 +- 3 files changed, 133 insertions(+), 90 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch create mode 100644 leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch diff --git a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch b/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch deleted file mode 100644 index 36d4f085..00000000 --- a/leaf-server/minecraft-patches/features/0168-Alternative-join-logic.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Thu, 8 May 2025 11:14:06 +0200 -Subject: [PATCH] Alternative join logic - - -diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..7423729fc9a6501c3a6408442ce28066067ce865 100644 ---- a/net/minecraft/network/Connection.java -+++ b/net/minecraft/network/Connection.java -@@ -341,35 +341,54 @@ public class Connection extends SimpleChannelInboundHandler> { - this.validateListener(protocolInfo, packetInfo); - if (protocolInfo.flow() != this.getReceiving()) { - throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); -+ } -+ this.packetListener = packetInfo; -+ this.disconnectListener = null; -+ UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); -+ BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); -+ if (bundlerInfo != null) { -+ PacketBundlePacker packetBundlePacker = new PacketBundlePacker(bundlerInfo); -+ inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", (ChannelHandler)packetBundlePacker)); -+ } -+ ChannelFuture future = this.channel.writeAndFlush(inboundConfigurationTask); -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin) { -+ Connection.syncAfterConfigurationChange(future); - } else { -- this.packetListener = packetInfo; -- this.disconnectListener = null; -- UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); -- BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); -- if (bundlerInfo != null) { -- PacketBundlePacker packetBundlePacker = new PacketBundlePacker(bundlerInfo); -- inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", packetBundlePacker)); -- } -- -- syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); -+ java.util.concurrent.CompletableFuture.runAsync(() -> { -+ try { -+ future.await(); -+ } catch (InterruptedException e) { -+ Thread.currentThread().interrupt(); -+ } -+ }); - } - } - - public void setupOutboundProtocol(ProtocolInfo protocolInfo) { -+ boolean flag; - if (protocolInfo.flow() != this.getSending()) { - throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); -+ } -+ UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); -+ BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); -+ if (bundlerInfo != null) { -+ PacketBundleUnpacker packetBundleUnpacker = new PacketBundleUnpacker(bundlerInfo); -+ outboundConfigurationTask = outboundConfigurationTask.andThen(context -> context.pipeline().addAfter("encoder", "unbundler", (ChannelHandler)packetBundleUnpacker)); -+ } -+ boolean bl = flag = protocolInfo.id() == ConnectionProtocol.LOGIN; -+ ChannelFuture future = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> { -+ this.sendLoginDisconnect = flag; -+ })); -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.AlternativeJoin) { -+ Connection.syncAfterConfigurationChange(future); - } else { -- UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); -- BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); -- if (bundlerInfo != null) { -- PacketBundleUnpacker packetBundleUnpacker = new PacketBundleUnpacker(bundlerInfo); -- outboundConfigurationTask = outboundConfigurationTask.andThen( -- context -> context.pipeline().addAfter("encoder", "unbundler", packetBundleUnpacker) -- ); -- } -- -- boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; -- syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); -+ java.util.concurrent.CompletableFuture.runAsync(() -> { -+ try { -+ future.await(); -+ } catch (InterruptedException e) { -+ Thread.currentThread().interrupt(); -+ } -+ }); - } - } - diff --git a/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch new file mode 100644 index 00000000..a2a45ac5 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch @@ -0,0 +1,129 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 9 May 2025 16:55:34 +0900 +Subject: [PATCH] async switch connection state + + +diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..624f1782a83ea0f8764b78819c23229fbfe51f49 100644 +--- a/net/minecraft/network/Connection.java ++++ b/net/minecraft/network/Connection.java +@@ -337,11 +337,17 @@ public class Connection extends SimpleChannelInboundHandler> { + } + } + ++ // Leaf start + public void setupInboundProtocol(ProtocolInfo protocolInfo, T packetInfo) { + this.validateListener(protocolInfo, packetInfo); + if (protocolInfo.flow() != this.getReceiving()) { + throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); + } else { ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { ++ this.channel.config().setAutoRead(false); ++ } ++ } + this.packetListener = packetInfo; + this.disconnectListener = null; + UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); +@@ -351,7 +357,14 @@ public class Connection extends SimpleChannelInboundHandler> { + inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", packetBundlePacker)); + } + +- syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); ++ var cf = this.channel.writeAndFlush(inboundConfigurationTask); ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { ++ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); ++ return; ++ } ++ } ++ syncAfterConfigurationChange(cf); + } + } + +@@ -369,9 +382,17 @@ public class Connection extends SimpleChannelInboundHandler> { + } + + boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; +- syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); ++ var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { ++ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); ++ return; ++ } ++ } ++ syncAfterConfigurationChange(cf); + } + } ++ // Leaf end + + public void setListenerForServerboundHandshake(PacketListener packetListener) { + if (this.packetListener != null) { +diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java +index 2e9eb04c7c4342393c05339906c267bca9ff29b1..3608d499b1bcca1f6507bf58cd307ae5d9a0bca1 100644 +--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java +@@ -140,12 +140,23 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis + } + } + ++ private volatile boolean changedState = false; // Leaf + @Override + public void handleConfigurationFinished(ServerboundFinishConfigurationPacket packet) { +- PacketUtils.ensureRunningOnSameThread(packet, this, this.server); +- this.finishCurrentTask(JoinWorldTask.TYPE); +- this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); +- ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ if (!changedState) { ++ this.finishCurrentTask(JoinWorldTask.TYPE); ++ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); ++ } ++ changedState = true; ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ } else { ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ this.finishCurrentTask(JoinWorldTask.TYPE); ++ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); ++ } ++ // Leaf end + try { + PlayerList playerList = this.server.getPlayerList(); + if (playerList.getPlayer(this.gameProfile.getId()) != null) { +diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index e9ce273812259627b61824ca4ffe83d301a4d946..0b5c9d55a7638c894a27eaeba8c98154a0b1380a 100644 +--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -472,11 +472,26 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); + } + ++ private volatile boolean changedState = false; // Leaf + @Override + public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { +- PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit +- Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); +- this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ if (!changedState) { ++ Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); ++ this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ } ++ changedState = true; ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ } else { ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); ++ this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ } ++ // PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit ++ // Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); ++ // this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ // Leaf end + CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); + ServerConfigurationPacketListenerImpl serverConfigurationPacketListenerImpl = new ServerConfigurationPacketListenerImpl( + this.server, this.connection, commonListenerCookie, this.player // CraftBukkit diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java index b9f86891..46f1154a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java @@ -9,12 +9,12 @@ public class AlternativeJoin extends ConfigModules { return EnumConfigCategory.NETWORK.getBaseKeyName(); } - public static boolean AlternativeJoin = true; + public static boolean enabled = false; @Override public void onLoaded() { - AlternativeJoin = config.getBoolean(getBasePath() + ".alternative-join", AlternativeJoin, config.pickStringRegionBased( - "Use alternative login logic to skip synchronization.", - "使用替代登录逻辑以跳过同步。")); + enabled = config.getBoolean(getBasePath() + ".async-switch-state", enabled, config.pickStringRegionBased( + "Async switch connection state", + "...")); } } From 01a465c7591e7d0411f0a625aa247c550bb310ad Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 9 May 2025 23:28:05 +0900 Subject: [PATCH 29/81] fix packets sent before switch connection state --- .../0168-async-switch-connection-state.patch | 147 +++++++++++------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch index a2a45ac5..a0894663 100644 --- a/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch @@ -5,56 +5,74 @@ Subject: [PATCH] async switch connection state diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..624f1782a83ea0f8764b78819c23229fbfe51f49 100644 +index f998cf8d70302a21289de4d84b46d322d0b8a8fe..32f26640664135c9f7f45f8b204b7ff412fe343e 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -337,11 +337,17 @@ public class Connection extends SimpleChannelInboundHandler> { - } - } - -+ // Leaf start - public void setupInboundProtocol(ProtocolInfo protocolInfo, T packetInfo) { - this.validateListener(protocolInfo, packetInfo); +@@ -342,6 +342,11 @@ public class Connection extends SimpleChannelInboundHandler> { if (protocolInfo.flow() != this.getReceiving()) { throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); } else { -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { -+ this.channel.config().setAutoRead(false); -+ } ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { ++ this.channel.config().setAutoRead(false); + } ++ // Leaf end this.packetListener = packetInfo; this.disconnectListener = null; UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); -@@ -351,7 +357,14 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -351,7 +356,14 @@ public class Connection extends SimpleChannelInboundHandler> { inboundConfigurationTask = inboundConfigurationTask.andThen(context -> context.pipeline().addAfter("decoder", "bundler", packetBundlePacker)); } - syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); ++ // Leaf start + var cf = this.channel.writeAndFlush(inboundConfigurationTask); -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { -+ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); -+ return; -+ } ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { ++ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); ++ return; + } + syncAfterConfigurationChange(cf); ++ // Leaf end } } -@@ -369,9 +382,17 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -369,9 +381,41 @@ public class Connection extends SimpleChannelInboundHandler> { } boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; - syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); + var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); ++ // Leaf start + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { + if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { -+ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); -+ return; ++ throw new IllegalStateException("Thread failed netty thread check: Switching outbound protocol state use setupOutboundProtocolAsync instead"); + } + } ++ // Leaf end + syncAfterConfigurationChange(cf); ++ } ++ } ++ // Leaf start ++ public @Nullable ChannelFuture setupOutboundProtocolAsync(ProtocolInfo protocolInfo) { ++ if (protocolInfo.flow() != this.getSending()) { ++ throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); ++ } else { ++ UnconfiguredPipelineHandler.OutboundConfigurationTask outboundConfigurationTask = UnconfiguredPipelineHandler.setupOutboundProtocol(protocolInfo); ++ BundlerInfo bundlerInfo = protocolInfo.bundlerInfo(); ++ if (bundlerInfo != null) { ++ PacketBundleUnpacker packetBundleUnpacker = new PacketBundleUnpacker(bundlerInfo); ++ outboundConfigurationTask = outboundConfigurationTask.andThen( ++ context -> context.pipeline().addAfter("encoder", "unbundler", packetBundleUnpacker) ++ ); ++ } ++ ++ boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; ++ var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); ++ return cf; ++ } ++ return null; } } + // Leaf end @@ -62,68 +80,79 @@ index f998cf8d70302a21289de4d84b46d322d0b8a8fe..624f1782a83ea0f8764b78819c23229f public void setListenerForServerboundHandshake(PacketListener packetListener) { if (this.packetListener != null) { diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java -index 2e9eb04c7c4342393c05339906c267bca9ff29b1..3608d499b1bcca1f6507bf58cd307ae5d9a0bca1 100644 +index 2e9eb04c7c4342393c05339906c267bca9ff29b1..c70d5a0db1dfd01eab323aefd07d6e81dd188927 100644 --- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java -@@ -140,12 +140,23 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis +@@ -140,11 +140,32 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis } } -+ private volatile boolean changedState = false; // Leaf ++ private volatile boolean changingState = false; // Leaf @Override public void handleConfigurationFinished(ServerboundFinishConfigurationPacket packet) { -- PacketUtils.ensureRunningOnSameThread(packet, this, this.server); -- this.finishCurrentTask(JoinWorldTask.TYPE); -- this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); -- + // Leaf start -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (!changedState) { -+ this.finishCurrentTask(JoinWorldTask.TYPE); -+ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); -+ } -+ changedState = true; -+ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); -+ } else { -+ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && !changingState) { ++ changingState = true; + this.finishCurrentTask(JoinWorldTask.TYPE); -+ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); ++ this.connection.setupOutboundProtocolAsync(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))).addListener(l -> { ++ try { ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ } catch (net.minecraft.server.RunningOnDifferentThreadException ignored) { ++ } catch ( ++ io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop ++ } catch (java.util.concurrent.RejectedExecutionException var6) { ++ this.connection.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); ++ } catch (ClassCastException var7) { ++ LOGGER.error("Received {} that couldn't be processed", packet.getClass(), var7); ++ this.connection.disconnect(Component.translatable("multiplayer.disconnect.invalid_packet")); ++ } ++ }); ++ return; + } + // Leaf end + PacketUtils.ensureRunningOnSameThread(packet, this, this.server); +- this.finishCurrentTask(JoinWorldTask.TYPE); +- this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.finishCurrentTask(JoinWorldTask.TYPE); } // Leaf ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); } // Leaf + try { PlayerList playerList = this.server.getPlayerList(); - if (playerList.getPlayer(this.gameProfile.getId()) != null) { diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index e9ce273812259627b61824ca4ffe83d301a4d946..0b5c9d55a7638c894a27eaeba8c98154a0b1380a 100644 +index e9ce273812259627b61824ca4ffe83d301a4d946..d7c113706d94ea510ddf7d0fffa927a15b198e9a 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -472,11 +472,26 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -472,11 +472,32 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } -+ private volatile boolean changedState = false; // Leaf ++ private volatile boolean changingState = false; // Leaf @Override public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { -- PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit -- Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); -- this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); + // Leaf start -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (!changedState) { -+ Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); -+ this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); -+ } -+ changedState = true; -+ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); -+ } else { -+ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); -+ Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); -+ this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && !changingState) { ++ changingState = true; ++ this.connection.setupOutboundProtocolAsync(ConfigurationProtocols.CLIENTBOUND).addListener(l -> { ++ try { ++ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); ++ } catch (net.minecraft.server.RunningOnDifferentThreadException ignored) { ++ } catch ( ++ io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop ++ } catch (java.util.concurrent.RejectedExecutionException var6) { ++ this.connection.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); ++ } catch (ClassCastException var7) { ++ LOGGER.error("Received {} that couldn't be processed", packet.getClass(), var7); ++ this.connection.disconnect(Component.translatable("multiplayer.disconnect.invalid_packet")); ++ } ++ }); ++ return; + } -+ // PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit -+ // Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); -+ // this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); + // Leaf end ++ + PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit + Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); +- this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); } // Leaf CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); ServerConfigurationPacketListenerImpl serverConfigurationPacketListenerImpl = new ServerConfigurationPacketListenerImpl( this.server, this.connection, commonListenerCookie, this.player // CraftBukkit From 7d9865678b37e095171f3866f8f10736edf3ac86 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 9 May 2025 23:56:41 +0200 Subject: [PATCH 30/81] Only tick items at hand --- .../0172-Only-tick-items-at-hand.patch | 45 +++++++++++++++++++ .../modules/opt/OptimizeItemTicking.java | 21 +++++++++ 2 files changed, 66 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java diff --git a/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch new file mode 100644 index 00000000..3ced9a74 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Fri, 9 May 2025 23:50:55 +0200 +Subject: [PATCH] Only tick items at hand + + +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 95e03045bcbee74ddac36ae36ce8c8c2f5769fa4..6eee16dccef1d0f04ba3532f5ee064478b842425 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -895,9 +895,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + super.tick(); + } + +- for (int i = 0; i < this.getInventory().getContainerSize(); i++) { +- ItemStack item = this.getInventory().getItem(i); +- if (!item.isEmpty()) { ++ if (org.dreeam.leaf.config.modules.opt.OptimizeItemTicking.onlyTickItemsInHand) { ++ this.synchronizeSpecialItemUpdates(this.getMainHandItem()); ++ this.synchronizeSpecialItemUpdates(this.getOffhandItem()); ++ } else { ++ for (int i = 0; i < this.getInventory().getContainerSize(); ++i) { ++ ItemStack item = this.getInventory().getItem(i); ++ if (item.isEmpty()) continue; + this.synchronizeSpecialItemUpdates(item); + } + } +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index ffff3c710f6a96de9372fe07ffa69e65d392273f..fb61bf4b69033d856e2ed424a0557d1f6a27e4e7 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -631,7 +631,12 @@ public abstract class Player extends LivingEntity { + } + + this.tickRegeneration(); +- this.inventory.tick(); ++ if (org.dreeam.leaf.config.modules.opt.OptimizeItemTicking.onlyTickItemsInHand) { ++ this.getMainHandItem().inventoryTick(this.level(), this, 0, true); ++ this.getOffhandItem().inventoryTick(this.level(), this, 0, true); ++ } else { ++ this.inventory.tick(); ++ } + this.oBob = this.bob; + if (this.abilities.flying && !this.isPassenger()) { + this.resetFallDistance(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java new file mode 100644 index 00000000..37cbc604 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizeItemTicking extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean onlyTickItemsInHand = true; + + @Override + public void onLoaded() { + onlyTickItemsInHand = config.getBoolean(getBasePath() + ".only-tick-items-in-hand", onlyTickItemsInHand, config.pickStringRegionBased(""" + Whether to only tick/update items in main hand and offhand instead of the entire inventory.""", + """ + 是否只对主手和副手中的物品进行tick/更新,而不是整个物品栏中的所有物品。""")); + } +} From 1cbabcaf0e3b16910d30198f15b9bed87d16f193 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 10 May 2025 00:25:32 +0200 Subject: [PATCH 31/81] dont enable it by default lmao --- .../org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java index 37cbc604..5de82881 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java @@ -9,7 +9,7 @@ public class OptimizeItemTicking extends ConfigModules { return EnumConfigCategory.PERF.getBaseKeyName(); } - public static boolean onlyTickItemsInHand = true; + public static boolean onlyTickItemsInHand = false; @Override public void onLoaded() { From 3a9112b16d8b7401f82ba092810904c8effebaa7 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 10 May 2025 11:07:42 +0900 Subject: [PATCH 32/81] update flush knockback --- ...e-and-flush-location-while-knockback.patch | 36 ----------- ...ove-and-flush-while-knockback-player.patch | 62 +++++++++++++++++++ 2 files changed, 62 insertions(+), 36 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch create mode 100644 leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch diff --git a/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch deleted file mode 100644 index 1dfa2928..00000000 --- a/leaf-server/minecraft-patches/features/0171-move-and-flush-location-while-knockback.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Thu, 8 May 2025 04:56:30 +0900 -Subject: [PATCH] move and flush location while knockback - - -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..ffff3c710f6a96de9372fe07ffa69e65d392273f 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -1415,6 +1415,25 @@ public abstract class Player extends LivingEntity { - ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); - target.hurtMarked = false; - target.setDeltaMovement(deltaMovement); -+ -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { -+ ServerPlayer targetPlayer = (ServerPlayer) target; -+ Vec3 before = targetPlayer.getDeltaMovement(); -+ targetPlayer.aiStep(); -+ targetPlayer.setDeltaMovement(before); -+ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); -+ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ targetPlayer.connection.resumeFlushing(); -+ targetPlayer.hasImpulse = true; -+ if (this instanceof ServerPlayer player1) { -+ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); -+ player1.connection.resumeFlushing(); -+ player1.hasImpulse = true; -+ } -+ } -+ // Leaf end - } - // CraftBukkit end - } diff --git a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch new file mode 100644 index 00000000..ebcf857c --- /dev/null +++ b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Thu, 8 May 2025 04:56:30 +0900 +Subject: [PATCH] serverside move and flush while knockback player + + +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 405b8da8b886b5caac7ed774472e106374c42185..0f3ec730ddffc6af8e9a556e303c653cdb8226b9 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -316,6 +316,7 @@ public class ServerGamePacketListenerImpl + private boolean waitingForSwitchToConfig; + private static final int MAX_SIGN_LINE_LENGTH = Integer.getInteger("Paper.maxSignLength", 80); // Paper - Limit client sign length + private final io.papermc.paper.event.packet.ClientTickEndEvent tickEndEvent; // Paper - add client tick end event ++ public int allowMoveWrong = 0; // Leaf + + public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie cookie) { + super(server, connection, cookie, player); // CraftBukkit +@@ -1526,6 +1527,7 @@ public class ServerGamePacketListenerImpl + d7 = d3 * d3 + d4 * d4 + d5 * d5; + boolean movedWrongly = false; // Paper - Add fail move event; rename + if (!this.player.isChangingDimension() ++ && (this.allowMoveWrong == 0 || d7 > 3.0) // Leaf + && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot + && !this.player.isSleeping() + && !this.player.gameMode.isCreative() +@@ -1540,6 +1542,7 @@ public class ServerGamePacketListenerImpl + LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), verticalDelta); // Purpur - AFK API + } // Paper + } ++ if (this.allowMoveWrong != 0) this.allowMoveWrong--; // Leaf + + // Paper start - Add fail move event + // Paper start - optimise out extra getCubes +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 477455fdfcc591a89823e88983eb12dabb078d9b..f427606d98b8e72638291a9db4077c8e71dce9a8 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1414,6 +1414,23 @@ public abstract class Player extends LivingEntity { + if (!cancelled) { + ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); + target.hurtMarked = false; ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { ++ ServerPlayer targetPlayer = (ServerPlayer) target; ++ targetPlayer.connection.allowMoveWrong = 3; ++ this.travel(getDeltaMovement()); ++ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); ++ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ targetPlayer.connection.connection.flushChannel(); ++ targetPlayer.hasImpulse = true; ++ if (this instanceof ServerPlayer player1) { ++ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); ++ player1.connection.connection.flushChannel(); ++ player1.hasImpulse = true; ++ } ++ } ++ // Leaf end + target.setDeltaMovement(deltaMovement); + } + // CraftBukkit end From d60ba2ebac481c639f932e73c77122480a48ea0f Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Fri, 9 May 2025 23:15:14 -0400 Subject: [PATCH 33/81] micro opt: Remove useless stream --- .../features/0162-paw-optimization.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch b/leaf-server/minecraft-patches/features/0162-paw-optimization.patch index aa641437..d2e6630c 100644 --- a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0162-paw-optimization.patch @@ -212,3 +212,16 @@ index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..124624d9c8cc292fcedb5652542b7d9d int floor = Mth.floor(x); int floor1 = Mth.floor(y); int floor2 = Mth.floor(z); +diff --git a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java +index 03ec2264b19e1794b609fe09d1ceaba4e0c4d669..3f38fe0140d13c7c356340ba06b55469ede0a1ad 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java +@@ -48,7 +48,7 @@ public class DesertPyramidStructure extends SinglePieceStructure { + } + } + +- ObjectArrayList list = new ObjectArrayList<>(set.stream().toList()); ++ ObjectArrayList list = new ObjectArrayList<>(set); // Leaf - paw optimization - TODO: use array + RandomSource randomSource = RandomSource.create(level.getSeed()).forkPositional().at(pieces.calculateBoundingBox().getCenter()); + Util.shuffle(list, randomSource); + int min = Math.min(set.size(), randomSource.nextInt(5, 8)); From 66a00d3dc58ccc59d18b6b1c96f42e8c69801b3e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 10 May 2025 14:25:53 +0900 Subject: [PATCH 34/81] fix flush-knockback target not move --- ...171-serverside-move-and-flush-while-knockback-player.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch index ebcf857c..900dc10c 100644 --- a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch +++ b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch @@ -33,7 +33,7 @@ index 405b8da8b886b5caac7ed774472e106374c42185..0f3ec730ddffc6af8e9a556e303c653c // Paper start - Add fail move event // Paper start - optimise out extra getCubes diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..f427606d98b8e72638291a9db4077c8e71dce9a8 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..46b1e4d5cbb9db0402b9901a3879272da5e1dfe6 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java @@ -1414,6 +1414,23 @@ public abstract class Player extends LivingEntity { @@ -44,7 +44,7 @@ index 477455fdfcc591a89823e88983eb12dabb078d9b..f427606d98b8e72638291a9db4077c8e + if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { + ServerPlayer targetPlayer = (ServerPlayer) target; + targetPlayer.connection.allowMoveWrong = 3; -+ this.travel(getDeltaMovement()); ++ targetPlayer.travel(targetPlayer.getDeltaMovement()); + targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); + targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); + targetPlayer.connection.connection.flushChannel(); From edab752b3aff2f6d19ac68234e7da754862ada7e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 10 May 2025 14:35:32 +0900 Subject: [PATCH 35/81] [ci skip] cleanup --- ...ove-and-flush-while-knockback-player.patch | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch index 900dc10c..23832af6 100644 --- a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch +++ b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch @@ -4,46 +4,17 @@ Date: Thu, 8 May 2025 04:56:30 +0900 Subject: [PATCH] serverside move and flush while knockback player -diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 405b8da8b886b5caac7ed774472e106374c42185..0f3ec730ddffc6af8e9a556e303c653cdb8226b9 100644 ---- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -316,6 +316,7 @@ public class ServerGamePacketListenerImpl - private boolean waitingForSwitchToConfig; - private static final int MAX_SIGN_LINE_LENGTH = Integer.getInteger("Paper.maxSignLength", 80); // Paper - Limit client sign length - private final io.papermc.paper.event.packet.ClientTickEndEvent tickEndEvent; // Paper - add client tick end event -+ public int allowMoveWrong = 0; // Leaf - - public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie cookie) { - super(server, connection, cookie, player); // CraftBukkit -@@ -1526,6 +1527,7 @@ public class ServerGamePacketListenerImpl - d7 = d3 * d3 + d4 * d4 + d5 * d5; - boolean movedWrongly = false; // Paper - Add fail move event; rename - if (!this.player.isChangingDimension() -+ && (this.allowMoveWrong == 0 || d7 > 3.0) // Leaf - && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot - && !this.player.isSleeping() - && !this.player.gameMode.isCreative() -@@ -1540,6 +1542,7 @@ public class ServerGamePacketListenerImpl - LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), verticalDelta); // Purpur - AFK API - } // Paper - } -+ if (this.allowMoveWrong != 0) this.allowMoveWrong--; // Leaf - - // Paper start - Add fail move event - // Paper start - optimise out extra getCubes diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..46b1e4d5cbb9db0402b9901a3879272da5e1dfe6 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..41fbd978632572636ca25a279fd15f818fa4eb22 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -1414,6 +1414,23 @@ public abstract class Player extends LivingEntity { +@@ -1414,6 +1414,22 @@ public abstract class Player extends LivingEntity { if (!cancelled) { ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); target.hurtMarked = false; + // Leaf start + if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { + ServerPlayer targetPlayer = (ServerPlayer) target; -+ targetPlayer.connection.allowMoveWrong = 3; + targetPlayer.travel(targetPlayer.getDeltaMovement()); + targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); + targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); From 184c2221b2069e0fa0e34fd7b9a5c8cc44e32204 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 10 May 2025 23:29:35 +0900 Subject: [PATCH 36/81] update flush knockback --- ...lush-position-while-knockback-player.patch | 44 +++++++++++++++++++ ...ove-and-flush-while-knockback-player.patch | 33 -------------- .../config/modules/gameplay/Knockback.java | 1 - 3 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch delete mode 100644 leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch diff --git a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch new file mode 100644 index 00000000..02596fc5 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Thu, 8 May 2025 04:56:30 +0900 +Subject: [PATCH] send and flush position while knockback player + + +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 405b8da8b886b5caac7ed774472e106374c42185..0e57a1799731e933f155fd694b224144da66977c 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1674,6 +1674,12 @@ public class ServerGamePacketListenerImpl + this.lastGoodX = this.player.getX(); + this.lastGoodY = this.player.getY(); + this.lastGoodZ = this.player.getZ(); ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this.player.lastHurtByPlayer instanceof ServerPlayer hurtBy && hurtBy.distanceToSqr(this.player) <= 1024D) { ++ hurtBy.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this.player)); ++ hurtBy.connection.connection.flushChannel(); ++ } ++ // Leaf end + } else { + this.internalTeleport(x, y, z, f, f1); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. + this.player.doCheckFallDamage(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z, packet.isOnGround()); +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 477455fdfcc591a89823e88983eb12dabb078d9b..4996f5c073443d5f93a8f05bc7a0adfe0c3713b5 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1480,6 +1480,16 @@ public abstract class Player extends LivingEntity { + } + + this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this instanceof ServerPlayer player1 && target instanceof ServerPlayer target1) { ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); ++ target1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ player1.hasImpulse = true; ++ target1.hasImpulse = true; ++ player1.connection.connection.flushChannel(); ++ target1.connection.connection.flushChannel(); ++ } ++ // Leaf end + } else { + this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility + // CraftBukkit start - resync on cancelled event diff --git a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch deleted file mode 100644 index 23832af6..00000000 --- a/leaf-server/minecraft-patches/features/0171-serverside-move-and-flush-while-knockback-player.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Thu, 8 May 2025 04:56:30 +0900 -Subject: [PATCH] serverside move and flush while knockback player - - -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..41fbd978632572636ca25a279fd15f818fa4eb22 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -1414,6 +1414,22 @@ public abstract class Player extends LivingEntity { - if (!cancelled) { - ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); - target.hurtMarked = false; -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { -+ ServerPlayer targetPlayer = (ServerPlayer) target; -+ targetPlayer.travel(targetPlayer.getDeltaMovement()); -+ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); -+ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ targetPlayer.connection.connection.flushChannel(); -+ targetPlayer.hasImpulse = true; -+ if (this instanceof ServerPlayer player1) { -+ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); -+ player1.connection.connection.flushChannel(); -+ player1.hasImpulse = true; -+ } -+ } -+ // Leaf end - target.setDeltaMovement(deltaMovement); - } - // CraftBukkit end diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java index b0610c61..1ffed0e9 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java @@ -13,7 +13,6 @@ public class Knockback extends ConfigModules { public static boolean snowballCanKnockback = false; public static boolean eggCanKnockback = false; public static boolean canPlayerKnockbackZombie = true; - @Experimental public static boolean flushKnockback = false; @Override From 6a59f9e8322fd75e390fe9bdc95a7768c682ff7e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 11 May 2025 01:34:16 +0900 Subject: [PATCH 37/81] rollback flush knockback to prev version --- ...lush-position-while-knockback-player.patch | 60 ++++++++----------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch index 02596fc5..37bd6186 100644 --- a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch +++ b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch @@ -4,41 +4,33 @@ Date: Thu, 8 May 2025 04:56:30 +0900 Subject: [PATCH] send and flush position while knockback player -diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 405b8da8b886b5caac7ed774472e106374c42185..0e57a1799731e933f155fd694b224144da66977c 100644 ---- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1674,6 +1674,12 @@ public class ServerGamePacketListenerImpl - this.lastGoodX = this.player.getX(); - this.lastGoodY = this.player.getY(); - this.lastGoodZ = this.player.getZ(); -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this.player.lastHurtByPlayer instanceof ServerPlayer hurtBy && hurtBy.distanceToSqr(this.player) <= 1024D) { -+ hurtBy.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this.player)); -+ hurtBy.connection.connection.flushChannel(); -+ } -+ // Leaf end - } else { - this.internalTeleport(x, y, z, f, f1); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. - this.player.doCheckFallDamage(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z, packet.isOnGround()); diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..4996f5c073443d5f93a8f05bc7a0adfe0c3713b5 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..789eed2336c31c486ea09eff7744d2e2146506cc 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -1480,6 +1480,16 @@ public abstract class Player extends LivingEntity { - } +@@ -1411,6 +1411,25 @@ public abstract class Player extends LivingEntity { + player.setVelocity(event.getVelocity()); + } - this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this instanceof ServerPlayer player1 && target instanceof ServerPlayer target1) { -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); -+ target1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ player1.hasImpulse = true; -+ target1.hasImpulse = true; -+ player1.connection.connection.flushChannel(); -+ target1.connection.connection.flushChannel(); -+ } -+ // Leaf end - } else { - this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility - // CraftBukkit start - resync on cancelled event ++ ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { ++ ServerPlayer target1 = (ServerPlayer) target; ++ Vec3 before = target1.getDeltaMovement(); ++ target1.aiStep(); ++ target1.setDeltaMovement(before); ++ target1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); ++ target1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ target1.connection.resumeFlushing(); ++ target1.hasImpulse = true; ++ if (this instanceof ServerPlayer player1) { ++ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(target1)); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); ++ player1.connection.resumeFlushing(); ++ player1.hasImpulse = true; ++ } ++ } ++ // Leaf end + if (!cancelled) { + ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); + target.hurtMarked = false; From 55a5cdafd089c7e76e4da6248a83434bae32cc7d Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 10 May 2025 21:53:32 +0200 Subject: [PATCH 38/81] [ci skip] move fastBitRadixSort to a dedicated method --- ...ntities-in-NearestLivingEntitySensor.patch | 100 ++------------- .../dreeam/leaf/util/fastBitRadixSort.java | 115 ++++++++++++++++++ 2 files changed, 128 insertions(+), 87 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 2bb35dff..9a7d0adc 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,116 +12,42 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..83af90c7a2e425b775abd7907895d211ced07955 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..3fc212c10d230f027292865a214a8164a84e284a 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -13,18 +13,102 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; +@@ -13,17 +13,30 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; import net.minecraft.world.phys.AABB; public class NearestLivingEntitySensor extends Sensor { + -+ // Leaf start - Optimized entity sorting with buffer reuse -+ private static final int SMALL_ARRAY_THRESHOLD = 2; -+ private LivingEntity[] entityBuffer = new LivingEntity[0]; -+ private long[] bitsBuffer = new long[0]; ++ // Leaf start - Smart sort entities in NearestLivingEntitySensor ++ private final org.dreeam.leaf.util.fastBitRadixSort sorter; ++ // Leaf end - Smart sort entities in NearestLivingEntitySensor ++ public NearestLivingEntitySensor() { ++ this.sorter = new org.dreeam.leaf.util.fastBitRadixSort(); ++ } + @Override protected void doTick(ServerLevel level, T entity) { -- double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE); -- AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); + double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE); ++ double rangeSqr = attributeValue * attributeValue; + AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); - List entitiesOfClass = level.getEntitiesOfClass( - LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() -+ double range = entity.getAttributeValue(Attributes.FOLLOW_RANGE); -+ double rangeSqr = range * range; -+ AABB aabb = entity.getBoundingBox().inflate(range, range, range); + + List entities = level.getEntitiesOfClass( + LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr ); - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); + -+ LivingEntity[] sorted = smartSort(entities, entity); ++ LivingEntity[] sorted = this.sorter.sort(entities, entity); + List sortedList = java.util.Arrays.asList(sorted); + Brain brain = entity.getBrain(); - brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, entitiesOfClass); - brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, entitiesOfClass)); + brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, sortedList); -+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, -+ new NearestVisibleLivingEntities(level, entity, sortedList)); -+ } -+ -+ private LivingEntity[] smartSort(List entities, T reference) { -+ int size = entities.size(); -+ if (size <= 1) return entities.toArray(new LivingEntity[0]); -+ -+ if (entityBuffer.length < size) { -+ entityBuffer = new LivingEntity[size]; -+ bitsBuffer = new long[size]; -+ } -+ -+ for (int i = 0; i < size; i++) { -+ LivingEntity e = entities.get(i); -+ entityBuffer[i] = e; -+ bitsBuffer[i] = Double.doubleToRawLongBits(reference.distanceToSqr(e)); -+ } -+ -+ fastRadixSort(entityBuffer, bitsBuffer, 0, size - 1, 62); -+ -+ return java.util.Arrays.copyOf(entityBuffer, size); -+ } -+ -+ private void fastRadixSort(LivingEntity[] ents, long[] bits, int low, int high, int bit) { -+ if (bit < 0 || low >= high) return; -+ -+ if (high - low <= SMALL_ARRAY_THRESHOLD) { -+ insertionSort(ents, bits, low, high); -+ return; -+ } -+ -+ int i = low, j = high; -+ final long mask = 1L << bit; -+ -+ while (i <= j) { -+ while (i <= j && (bits[i] & mask) == 0) i++; -+ while (i <= j && (bits[j] & mask) != 0) j--; -+ -+ if (i < j) { -+ swap(ents, bits, i++, j--); -+ } -+ } -+ -+ if (low < j) fastRadixSort(ents, bits, low, j, bit - 1); -+ if (i < high) fastRadixSort(ents, bits, i, high, bit - 1); -+ } -+ -+ private void insertionSort(LivingEntity[] ents, long[] bits, int low, int high) { -+ for (int i = low + 1; i <= high; i++) { -+ int j = i; -+ LivingEntity e = ents[j]; -+ long b = bits[j]; -+ -+ while (j > low && bits[j - 1] > b) { -+ ents[j] = ents[j - 1]; -+ bits[j] = bits[j - 1]; -+ j--; -+ } -+ -+ ents[j] = e; -+ bits[j] = b; -+ } -+ } -+ -+ private void swap(LivingEntity[] ents, long[] bits, int a, int b) { -+ LivingEntity te = ents[a]; -+ ents[a] = ents[b]; -+ ents[b] = te; -+ -+ long tb = bits[a]; -+ bits[a] = bits[b]; -+ bits[b] = tb; ++ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, sortedList)); } -+ // Leaf end - Optimized entity sorting with buffer reuse @Override - public Set> requires() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java new file mode 100644 index 00000000..ee9fad2e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java @@ -0,0 +1,115 @@ +package org.dreeam.leaf.util; + +import java.util.List; +import java.util.Arrays; // For Arrays.copyOf +import net.minecraft.world.entity.LivingEntity; + +public class fastBitRadixSort { + + private static final int SMALL_ARRAY_THRESHOLD = 2; + private LivingEntity[] entityBuffer = new LivingEntity[0]; + private long[] bitsBuffer = new long[0]; + + /** + * Sorts a list of LivingEntity objects based on their squared distance + * to a reference entity using a fast radix sort algorithm. + **/ + public LivingEntity[] sort( + List entities, + T_REF referenceEntity + ) { + int size = entities.size(); + if (size <= 1) { + return entities.toArray(new LivingEntity[0]); + } + + if (this.entityBuffer.length < size) { + this.entityBuffer = new LivingEntity[size]; + this.bitsBuffer = new long[size]; + } + for (int i = 0; i < size; i++) { + LivingEntity e = entities.get(i); + this.entityBuffer[i] = e; + this.bitsBuffer[i] = Double.doubleToRawLongBits( + referenceEntity.distanceToSqr(e) + ); + } + + // start from bit 62 (most significant for positive doubles, ignoring sign bit) + fastRadixSort(this.entityBuffer, this.bitsBuffer, 0, size - 1, 62); + return Arrays.copyOf(this.entityBuffer, size); + } + + private void fastRadixSort( + LivingEntity[] ents, + long[] bits, + int low, + int high, + int bit + ) { + if (bit < 0 || low >= high) { + return; // Base case: no bits left or subarray is trivial + } + + // For small subarrays, insertion sort is generally faster + if (high - low <= SMALL_ARRAY_THRESHOLD) { + insertionSort(ents, bits, low, high); + return; + } + + int i = low; + int j = high; + final long mask = 1L << bit; + + while (i <= j) { + while (i <= j && (bits[i] & mask) == 0) { + i++; + } + while (i <= j && (bits[j] & mask) != 0) { + j--; + } + if (i < j) { + swap(ents, bits, i++, j--); + } + } + + if (low < j) { + fastRadixSort(ents, bits, low, j, bit - 1); + } + if (i < high) { + fastRadixSort(ents, bits, i, high, bit - 1); + } + } + + private void insertionSort( + LivingEntity[] ents, + long[] bits, + int low, + int high + ) { + for (int i = low + 1; i <= high; i++) { + int j = i; + LivingEntity currentEntity = ents[j]; + long currentBits = bits[j]; + + while (j > low && bits[j - 1] > currentBits) { + ents[j] = ents[j - 1]; + bits[j] = bits[j - 1]; + j--; + } + + ents[j] = currentEntity; + bits[j] = currentBits; + } + } + + private void swap(LivingEntity[] ents, long[] bits, int a, int b) { + LivingEntity tempEntity = ents[a]; + ents[a] = ents[b]; + ents[b] = tempEntity; + + long tempBits = bits[a]; + bits[a] = bits[b]; + bits[b] = tempBits; + } +} From 06baf83a902aea3c7cf612ba8f46401c4a3bc93d Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 10 May 2025 22:53:54 +0200 Subject: [PATCH 39/81] Smart sort items in NearestItemSensor --- ...ntities-in-NearestLivingEntitySensor.patch | 13 +++-- ...mart-sort-items-in-NearestItemSensor.patch | 45 +++++++++++++++++ .../dreeam/leaf/util/fastBitRadixSort.java | 49 +++++++++---------- 3 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 9a7d0adc..1e87e011 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,10 +12,10 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..3fc212c10d230f027292865a214a8164a84e284a 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..a887bb8ed2318d6ee39be350a1d0e7223ecf3ff5 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -13,17 +13,30 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; +@@ -13,17 +13,28 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; import net.minecraft.world.phys.AABB; public class NearestLivingEntitySensor extends Sensor { @@ -34,13 +34,12 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..3fc212c10d230f027292865a214a8164 AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); - List entitiesOfClass = level.getEntitiesOfClass( - LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() -+ -+ List entities = level.getEntitiesOfClass( -+ LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr - ); +- ); - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); + -+ LivingEntity[] sorted = this.sorter.sort(entities, entity); ++ List entities = level.getEntitiesOfClass(LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr); ++ ++ LivingEntity[] sorted = this.sorter.sort(entities, entity, LivingEntity.class); + List sortedList = java.util.Arrays.asList(sorted); + Brain brain = entity.getBrain(); diff --git a/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch new file mode 100644 index 00000000..58bac1dd --- /dev/null +++ b/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 10 May 2025 22:04:42 +0200 +Subject: [PATCH] Smart sort items in NearestItemSensor + + +diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +index 09fd13e2d958da8326276c4dadf25bf488aff5ac..70036d073b644ff5f486491486c38db5df643107 100644 +--- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +@@ -6,6 +6,7 @@ import java.util.List; + import java.util.Optional; + import java.util.Set; + import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.Mob; + import net.minecraft.world.entity.ai.Brain; + import net.minecraft.world.entity.ai.memory.MemoryModuleType; +@@ -16,6 +17,8 @@ public class NearestItemSensor extends Sensor { + private static final long Y_RANGE = 16L; + public static final int MAX_DISTANCE_TO_WANTED_ITEM = 32; + ++ private final org.dreeam.leaf.util.fastBitRadixSort itemSorter; ++ public NearestItemSensor() {this.itemSorter = new org.dreeam.leaf.util.fastBitRadixSort();} + @Override + public Set> requires() { + return ImmutableSet.of(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM); +@@ -24,12 +27,13 @@ public class NearestItemSensor extends Sensor { + @Override + protected void doTick(ServerLevel level, Mob entity) { + Brain brain = entity.getBrain(); +- List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities +- entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); ++ ItemEntity[] sortedItems = this.itemSorter.sort(entitiesOfClass, entity, ItemEntity.class); ++ + // Paper start - Perf: remove streams from hot code + ItemEntity nearest = null; +- for (final ItemEntity itemEntity : entitiesOfClass) { +- if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities ++ for (final ItemEntity itemEntity : sortedItems) { ++ if (entity.hasLineOfSight(itemEntity)) { + nearest = itemEntity; + break; + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java index ee9fad2e..21b36d46 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java @@ -1,57 +1,55 @@ package org.dreeam.leaf.util; import java.util.List; -import java.util.Arrays; // For Arrays.copyOf -import net.minecraft.world.entity.LivingEntity; +import java.util.Arrays; +import net.minecraft.world.entity.Entity; +import java.lang.reflect.Array; // Required for Array.newInstance public class fastBitRadixSort { private static final int SMALL_ARRAY_THRESHOLD = 2; - private LivingEntity[] entityBuffer = new LivingEntity[0]; + private Entity[] entityBuffer = new Entity[0]; private long[] bitsBuffer = new long[0]; - /** - * Sorts a list of LivingEntity objects based on their squared distance - * to a reference entity using a fast radix sort algorithm. - **/ - public LivingEntity[] sort( - List entities, - T_REF referenceEntity - ) { + @SuppressWarnings("unchecked") + public T[] sort(List entities, T_REF referenceEntity, Class entityClass) { int size = entities.size(); if (size <= 1) { - return entities.toArray(new LivingEntity[0]); + T[] resultArray = (T[]) Array.newInstance(entityClass, size); + return entities.toArray(resultArray); } if (this.entityBuffer.length < size) { - this.entityBuffer = new LivingEntity[size]; + this.entityBuffer = new Entity[size]; this.bitsBuffer = new long[size]; } for (int i = 0; i < size; i++) { - LivingEntity e = entities.get(i); - this.entityBuffer[i] = e; + this.entityBuffer[i] = entities.get(i); this.bitsBuffer[i] = Double.doubleToRawLongBits( - referenceEntity.distanceToSqr(e) + referenceEntity.distanceToSqr(entities.get(i)) ); } - // start from bit 62 (most significant for positive doubles, ignoring sign bit) fastRadixSort(this.entityBuffer, this.bitsBuffer, 0, size - 1, 62); - return Arrays.copyOf(this.entityBuffer, size); + + T[] resultArray = (T[]) Array.newInstance(entityClass, size); + for (int i = 0; i < size; i++) { + resultArray[i] = entityClass.cast(this.entityBuffer[i]); + } + return resultArray; } private void fastRadixSort( - LivingEntity[] ents, + Entity[] ents, long[] bits, int low, int high, int bit ) { if (bit < 0 || low >= high) { - return; // Base case: no bits left or subarray is trivial + return; } - // For small subarrays, insertion sort is generally faster if (high - low <= SMALL_ARRAY_THRESHOLD) { insertionSort(ents, bits, low, high); return; @@ -82,14 +80,14 @@ public class fastBitRadixSort { } private void insertionSort( - LivingEntity[] ents, + Entity[] ents, long[] bits, int low, int high ) { for (int i = low + 1; i <= high; i++) { int j = i; - LivingEntity currentEntity = ents[j]; + Entity currentEntity = ents[j]; long currentBits = bits[j]; while (j > low && bits[j - 1] > currentBits) { @@ -97,14 +95,13 @@ public class fastBitRadixSort { bits[j] = bits[j - 1]; j--; } - ents[j] = currentEntity; bits[j] = currentBits; } } - private void swap(LivingEntity[] ents, long[] bits, int a, int b) { - LivingEntity tempEntity = ents[a]; + private void swap(Entity[] ents, long[] bits, int a, int b) { + Entity tempEntity = ents[a]; ents[a] = ents[b]; ents[b] = tempEntity; From 8c56c79a351a2626c1b12b99564f7e5247c820aa Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 11 May 2025 00:06:55 +0200 Subject: [PATCH 40/81] faster player movement checks --- ...0174-Optimise-player-movement-checks.patch | 30 +++++++++++++++++++ .../opt/OptimizePlayerMovementProcessing.java | 22 ++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java diff --git a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch new file mode 100644 index 00000000..8d96e41a --- /dev/null +++ b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 10 May 2025 23:18:17 +0200 +Subject: [PATCH] Optimise player movement checks + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 2786fbc43f238be058b153ba551f0a616c4a18f4..48c386360c331b8e1a44524951b74874a953df5e 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -1149,6 +1149,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return; + } + // Gale end - VMP - skip entity move if movement is zero ++ ++ + final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + if (this.noPhysics) { +@@ -1176,6 +1178,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + // Paper end + ++ if (!org.dreeam.leaf.config.modules.opt.OptimizePlayerMovementProcessing.enabled) { ++ movement = this.maybeBackOffFromEdge(movement, type); ++ } ++ + movement = this.maybeBackOffFromEdge(movement, type); + Vec3 vec3 = this.collide(movement); + double d = vec3.lengthSqr(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java new file mode 100644 index 00000000..3f407850 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java @@ -0,0 +1,22 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizePlayerMovementProcessing extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".optimize-player-movement", enabled, config.pickStringRegionBased(""" + Whether to optimize player movement processing by skipping unnecessary edge checks and avoiding redundant view distance updates.""", + """ + 是否优化玩家移动处理,跳过不必要的边缘检查并避免冗余的视距更新。""")); + + } +} From 068f7669c8d1b89b0b082c48834d5e8c41437dd1 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 11 May 2025 00:33:42 +0200 Subject: [PATCH 41/81] [ci skip] cleanup --- .../0174-Optimise-player-movement-checks.patch | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch index 8d96e41a..df6396fe 100644 --- a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch +++ b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch @@ -5,19 +5,10 @@ Subject: [PATCH] Optimise player movement checks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 2786fbc43f238be058b153ba551f0a616c4a18f4..48c386360c331b8e1a44524951b74874a953df5e 100644 +index 2786fbc43f238be058b153ba551f0a616c4a18f4..94414d33fa7bd20b4926cb4dcb966a15d86e545e 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1149,6 +1149,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return; - } - // Gale end - VMP - skip entity move if movement is zero -+ -+ - final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity - ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); - if (this.noPhysics) { -@@ -1176,6 +1178,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1176,6 +1176,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end From 37ebb20d7ee8461b21c12c8e6d28a7bc81d09e3f Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 11 May 2025 00:55:25 +0200 Subject: [PATCH 42/81] had to commit here, right --- ...0037-Optimise-player-movement-checks.patch | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch diff --git a/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch b/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch new file mode 100644 index 00000000..e28034f5 --- /dev/null +++ b/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 11 May 2025 00:37:44 +0200 +Subject: [PATCH] Optimise player movement checks + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java +index 94689e0342cf95dbedec955d67c95fa07a219678..26e4fe123c84e63ae4808facc33d6bbe94fc3133 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java +@@ -88,6 +88,9 @@ public abstract class SingleUserAreaMap { + if (fromX == NOT_SET) { + return false; + } ++ if (org.dreeam.leaf.config.modules.opt.OptimizePlayerMovementProcessing.enabled && fromX == toX && fromZ == toZ && oldViewDistance == newViewDistance) { ++ return true; ++ } + + this.lastChunkX = toX; + this.lastChunkZ = toZ; From 867391e7a549b9d3d146c76a54edfd703a58fd33 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 11 May 2025 02:17:49 -0400 Subject: [PATCH 43/81] Fix patch --- .../0174-Optimise-player-movement-checks.patch | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch index df6396fe..aad1cc16 100644 --- a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch +++ b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch @@ -5,17 +5,15 @@ Subject: [PATCH] Optimise player movement checks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 2786fbc43f238be058b153ba551f0a616c4a18f4..94414d33fa7bd20b4926cb4dcb966a15d86e545e 100644 +index 2786fbc43f238be058b153ba551f0a616c4a18f4..376d8cb183145955b887597ef7ae25ce761a795a 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1176,6 +1176,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1176,7 +1176,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end -+ if (!org.dreeam.leaf.config.modules.opt.OptimizePlayerMovementProcessing.enabled) { -+ movement = this.maybeBackOffFromEdge(movement, type); -+ } -+ - movement = this.maybeBackOffFromEdge(movement, type); +- movement = this.maybeBackOffFromEdge(movement, type); ++ if (!org.dreeam.leaf.config.modules.opt.OptimizePlayerMovementProcessing.enabled) movement = this.maybeBackOffFromEdge(movement, type); // Leaf - Optimise player movement checks Vec3 vec3 = this.collide(movement); double d = vec3.lengthSqr(); + if (d > 1.0E-7 || movement.lengthSqr() - d < 1.0E-7) { From 727766c5ef9da85a086e07a6889b58b5d9ccaa9f Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 11 May 2025 21:47:18 +0900 Subject: [PATCH 44/81] update flush knockback --- ...lush-position-while-knockback-player.patch | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch index 37bd6186..dee46232 100644 --- a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch +++ b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch @@ -5,32 +5,40 @@ Subject: [PATCH] send and flush position while knockback player diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..789eed2336c31c486ea09eff7744d2e2146506cc 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..fdc9c68a0ed30b8b342ceaca5d988e82524d6bf9 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -1411,6 +1411,25 @@ public abstract class Player extends LivingEntity { +@@ -1411,6 +1411,20 @@ public abstract class Player extends LivingEntity { player.setVelocity(event.getVelocity()); } + + // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && !cancelled && this instanceof ServerPlayer player1) { + ServerPlayer target1 = (ServerPlayer) target; -+ Vec3 before = target1.getDeltaMovement(); ++ var targetV = target1.getDeltaMovement(); + target1.aiStep(); -+ target1.setDeltaMovement(before); -+ target1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); ++ target1.setDeltaMovement(targetV); + target1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ target1.connection.resumeFlushing(); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); ++ player1.connection.send(new ClientboundSetEntityMotionPacket(target1)); ++ player1.hasImpulse = true; + target1.hasImpulse = true; -+ if (this instanceof ServerPlayer player1) { -+ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(target1)); -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); -+ player1.connection.resumeFlushing(); -+ player1.hasImpulse = true; -+ } + } + // Leaf end if (!cancelled) { ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); target.hurtMarked = false; +@@ -1480,6 +1494,12 @@ public abstract class Player extends LivingEntity { + } + + this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && target instanceof ServerPlayer target1 && this instanceof ServerPlayer player1) { ++ target1.connection.connection.flushChannel(); ++ player1.connection.connection.flushChannel(); ++ } ++ // Leaf end + } else { + this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility + // CraftBukkit start - resync on cancelled event From 130b55d7d4c34b4245c10c4b38974da7ccd5a67e Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 11 May 2025 21:25:14 +0200 Subject: [PATCH 45/81] if it aint broken, dont fix it --- ...tch => 0171-Only-tick-items-at-hand.patch} | 2 +- ...lush-position-while-knockback-player.patch | 44 ------------------- ...art-sort-items-in-NearestItemSensor.patch} | 0 ...173-Optimise-player-movement-checks.patch} | 0 ...e-and-flush-location-while-knockback.patch | 36 +++++++++++++++ .../config/modules/gameplay/Knockback.java | 1 + 6 files changed, 38 insertions(+), 45 deletions(-) rename leaf-server/minecraft-patches/features/{0172-Only-tick-items-at-hand.patch => 0171-Only-tick-items-at-hand.patch} (95%) delete mode 100644 leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch rename leaf-server/minecraft-patches/features/{0173-Smart-sort-items-in-NearestItemSensor.patch => 0172-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0174-Optimise-player-movement-checks.patch => 0173-Optimise-player-movement-checks.patch} (100%) create mode 100644 leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch index 3ced9a74..431f852c 100644 --- a/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch @@ -26,7 +26,7 @@ index 95e03045bcbee74ddac36ae36ce8c8c2f5769fa4..6eee16dccef1d0f04ba3532f5ee06447 } } diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index ffff3c710f6a96de9372fe07ffa69e65d392273f..fb61bf4b69033d856e2ed424a0557d1f6a27e4e7 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..9e62fe46eb9883ade81c1c9ff0f8220ed34b5d85 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java @@ -631,7 +631,12 @@ public abstract class Player extends LivingEntity { diff --git a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch b/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch deleted file mode 100644 index dee46232..00000000 --- a/leaf-server/minecraft-patches/features/0171-send-and-flush-position-while-knockback-player.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Thu, 8 May 2025 04:56:30 +0900 -Subject: [PATCH] send and flush position while knockback player - - -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..fdc9c68a0ed30b8b342ceaca5d988e82524d6bf9 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -1411,6 +1411,20 @@ public abstract class Player extends LivingEntity { - player.setVelocity(event.getVelocity()); - } - -+ -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && !cancelled && this instanceof ServerPlayer player1) { -+ ServerPlayer target1 = (ServerPlayer) target; -+ var targetV = target1.getDeltaMovement(); -+ target1.aiStep(); -+ target1.setDeltaMovement(targetV); -+ target1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(target1)); -+ player1.connection.send(new ClientboundSetEntityMotionPacket(target1)); -+ player1.hasImpulse = true; -+ target1.hasImpulse = true; -+ } -+ // Leaf end - if (!cancelled) { - ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); - target.hurtMarked = false; -@@ -1480,6 +1494,12 @@ public abstract class Player extends LivingEntity { - } - - this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && target instanceof ServerPlayer target1 && this instanceof ServerPlayer player1) { -+ target1.connection.connection.flushChannel(); -+ player1.connection.connection.flushChannel(); -+ } -+ // Leaf end - } else { - this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility - // CraftBukkit start - resync on cancelled event diff --git a/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch new file mode 100644 index 00000000..00f995a1 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 11 May 2025 19:45:58 +0200 +Subject: [PATCH] move and flush location while knockback + + +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 9e62fe46eb9883ade81c1c9ff0f8220ed34b5d85..fb61bf4b69033d856e2ed424a0557d1f6a27e4e7 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1420,6 +1420,25 @@ public abstract class Player extends LivingEntity { + ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); + target.hurtMarked = false; + target.setDeltaMovement(deltaMovement); ++ ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { ++ ServerPlayer targetPlayer = (ServerPlayer) target; ++ Vec3 before = targetPlayer.getDeltaMovement(); ++ targetPlayer.aiStep(); ++ targetPlayer.setDeltaMovement(before); ++ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); ++ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ targetPlayer.connection.resumeFlushing(); ++ targetPlayer.hasImpulse = true; ++ if (this instanceof ServerPlayer player1) { ++ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); ++ player1.connection.resumeFlushing(); ++ player1.hasImpulse = true; ++ } ++ } ++ // Leaf end + } + // CraftBukkit end + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java index 1ffed0e9..b0610c61 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java @@ -13,6 +13,7 @@ public class Knockback extends ConfigModules { public static boolean snowballCanKnockback = false; public static boolean eggCanKnockback = false; public static boolean canPlayerKnockbackZombie = true; + @Experimental public static boolean flushKnockback = false; @Override From af016b1178eb9c2675c05ed6120334c79c3628b1 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 11 May 2025 17:54:56 -0400 Subject: [PATCH 46/81] Temp fix Will fix soon when I have time --- .../features/0048-Cache-player-profileResult.patch | 4 ++-- .../main/java/org/dreeam/leaf/config/modules/misc/Cache.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch index 2b4d7548..9c7603ff 100644 --- a/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch +++ b/leaf-server/minecraft-patches/features/0048-Cache-player-profileResult.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cache player profileResult diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 069477e524a28b20a0289221858bdc802704a890..e9ce273812259627b61824ca4ffe83d301a4d946 100644 +index 069477e524a28b20a0289221858bdc802704a890..49f1743db193be1f10bfe6419231eb682e1068f7 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java @@ -71,6 +71,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, @@ -29,7 +29,7 @@ index 069477e524a28b20a0289221858bdc802704a890..e9ce273812259627b61824ca4ffe83d3 - .hasJoinedServer(string1, string, this.getAddress()); + // Leaf start - Cache player profileResult + ProfileResult profileResult; -+ if (org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResult) { ++ if (false) { // TODO + profileResult = playerProfileResultCache.getIfPresent(string1); + + if (profileResult == null) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/Cache.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/Cache.java index 132f820c..d7d6e7fc 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/Cache.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/Cache.java @@ -9,7 +9,7 @@ public class Cache extends ConfigModules { return EnumConfigCategory.MISC.getBaseKeyName() + ".cache"; } - public static boolean cachePlayerProfileResult = true; + public static boolean cachePlayerProfileResult = false; public static int cachePlayerProfileResultTimeout = 1440; @Override From aab1f8db99b8006903e219bc235bbfd2c0604b06 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Mon, 12 May 2025 14:56:01 +0900 Subject: [PATCH 47/81] cleanup and rebase --- .../0171-flush-location-while-knockback.patch | 37 +++++++++++++++++++ ...tch => 0172-Only-tick-items-at-hand.patch} | 2 +- ...art-sort-items-in-NearestItemSensor.patch} | 0 ...174-Optimise-player-movement-checks.patch} | 0 ...e-and-flush-location-while-knockback.patch | 36 ------------------ 5 files changed, 38 insertions(+), 37 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch rename leaf-server/minecraft-patches/features/{0171-Only-tick-items-at-hand.patch => 0172-Only-tick-items-at-hand.patch} (95%) rename leaf-server/minecraft-patches/features/{0172-Smart-sort-items-in-NearestItemSensor.patch => 0173-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0173-Optimise-player-movement-checks.patch => 0174-Optimise-player-movement-checks.patch} (100%) delete mode 100644 leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch new file mode 100644 index 00000000..27a24946 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 11 May 2025 19:45:58 +0200 +Subject: [PATCH] flush location while knockback + + +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index 477455fdfcc591a89823e88983eb12dabb078d9b..67c4cbb8ed6131b639b01e4fc0cbf56057a3fe03 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1412,6 +1412,13 @@ public abstract class Player extends LivingEntity { + } + + if (!cancelled) { ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && target instanceof ServerPlayer targetPlayer && this instanceof ServerPlayer player1) { ++ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); ++ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); ++ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); ++ } ++ // Leaf end + ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); + target.hurtMarked = false; + target.setDeltaMovement(deltaMovement); +@@ -1480,6 +1487,12 @@ public abstract class Player extends LivingEntity { + } + + this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this instanceof ServerPlayer player1 && target instanceof ServerPlayer target1) { ++ target1.connection.connection.flushChannel(); ++ player1.connection.connection.flushChannel(); ++ } ++ // Leaf end + } else { + this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility + // CraftBukkit start - resync on cancelled event diff --git a/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch index 431f852c..93a52c64 100644 --- a/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch @@ -26,7 +26,7 @@ index 95e03045bcbee74ddac36ae36ce8c8c2f5769fa4..6eee16dccef1d0f04ba3532f5ee06447 } } diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..9e62fe46eb9883ade81c1c9ff0f8220ed34b5d85 100644 +index 67c4cbb8ed6131b639b01e4fc0cbf56057a3fe03..8bab487a1d77d8227bf9426d0218e906db034982 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java @@ -631,7 +631,12 @@ public abstract class Player extends LivingEntity { diff --git a/leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch deleted file mode 100644 index 00f995a1..00000000 --- a/leaf-server/minecraft-patches/features/0174-move-and-flush-location-while-knockback.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Sun, 11 May 2025 19:45:58 +0200 -Subject: [PATCH] move and flush location while knockback - - -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 9e62fe46eb9883ade81c1c9ff0f8220ed34b5d85..fb61bf4b69033d856e2ed424a0557d1f6a27e4e7 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -1420,6 +1420,25 @@ public abstract class Player extends LivingEntity { - ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); - target.hurtMarked = false; - target.setDeltaMovement(deltaMovement); -+ -+ // Leaf start -+ if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback) { -+ ServerPlayer targetPlayer = (ServerPlayer) target; -+ Vec3 before = targetPlayer.getDeltaMovement(); -+ targetPlayer.aiStep(); -+ targetPlayer.setDeltaMovement(before); -+ targetPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this)); -+ targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); -+ targetPlayer.connection.resumeFlushing(); -+ targetPlayer.hasImpulse = true; -+ if (this instanceof ServerPlayer player1) { -+ player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); -+ player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); -+ player1.connection.resumeFlushing(); -+ player1.hasImpulse = true; -+ } -+ } -+ // Leaf end - } - // CraftBukkit end - } From 0893c933c9ca09bc637d58a849e4343bc0d1d0e5 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 13 May 2025 00:58:31 +0900 Subject: [PATCH 48/81] Updated Upstream (Gale) Paper Changes: PaperMC/Paper@c2bb144f Properly save level data async (#12530) PaperMC/Paper@e2ca4773 Remove simplify remote item matching option for now --- gradle.properties | 2 +- ...0007-Purpur-Server-Minecraft-Changes.patch | 38 +++++++++---------- .../0019-Slice-Smooth-Teleports.patch | 4 +- .../0044-Improve-Purpur-AFK-system.patch | 6 +-- .../features/0076-Fix-MC-119417.patch | 4 +- .../0079-Hide-specified-item-components.patch | 6 +-- .../0100-Smooth-teleport-config.patch | 6 +-- ...-eligible-players-for-despawn-checks.patch | 6 +-- ...-SparklyPaper-Parallel-world-ticking.patch | 16 ++++---- .../features/0165-Protocol-Core.patch | 6 +-- .../0166-Save-world-async-properly.patch | 20 ---------- ... => 0166-reduce-PlayerChunk-Updates.patch} | 0 ... 0167-async-switch-connection-state.patch} | 2 +- ...timise-BlockEntities-tickersInLevel.patch} | 0 ...e-cactus-can-even-survive-being-pla.patch} | 0 ...0170-flush-location-while-knockback.patch} | 0 ...tch => 0171-Only-tick-items-at-hand.patch} | 4 +- ...art-sort-items-in-NearestItemSensor.patch} | 0 ...173-Optimise-player-movement-checks.patch} | 0 .../GlobalConfiguration.java.patch | 11 ++++++ .../craftbukkit/entity/CraftPlayer.java.patch | 32 ++++++++++++++++ 21 files changed, 93 insertions(+), 70 deletions(-) delete mode 100644 leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch rename leaf-server/minecraft-patches/features/{0167-reduce-PlayerChunk-Updates.patch => 0166-reduce-PlayerChunk-Updates.patch} (100%) rename leaf-server/minecraft-patches/features/{0168-async-switch-connection-state.patch => 0167-async-switch-connection-state.patch} (99%) rename leaf-server/minecraft-patches/features/{0169-Optimise-BlockEntities-tickersInLevel.patch => 0168-Optimise-BlockEntities-tickersInLevel.patch} (100%) rename leaf-server/minecraft-patches/features/{0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch => 0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch} (100%) rename leaf-server/minecraft-patches/features/{0171-flush-location-while-knockback.patch => 0170-flush-location-while-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0172-Only-tick-items-at-hand.patch => 0171-Only-tick-items-at-hand.patch} (93%) rename leaf-server/minecraft-patches/features/{0173-Smart-sort-items-in-NearestItemSensor.patch => 0172-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0174-Optimise-player-movement-checks.patch => 0173-Optimise-player-movement-checks.patch} (100%) create mode 100644 leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch create mode 100644 leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch diff --git a/gradle.properties b/gradle.properties index 66d5cc50..a5f55fbf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=cn.dreeam.leaf mcVersion=1.21.4 version=1.21.4-R0.1-SNAPSHOT -galeCommit=d5d63524e02c6b7ff448ede990beb9edca897485 +galeCommit=e6d2efb8f8dc092ac55647c745440ea3d60f8797 org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch index 90075701..b9c9dfe9 100644 --- a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch +++ b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch @@ -724,7 +724,7 @@ index f262a7c5ae4e7d56f16f5c0f4f145a2e428abbe4..614c7d9f673c926562acc8fa3b378862 private JComponent buildOnboardingPanel() { String onboardingLink = "https://docs.papermc.io/paper/next-steps"; diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 101f1a87a5fe920b57a5179da41cc91d88afa32e..b49dd636e730f0c5b609df68ee51bcd12efc1eaa 100644 +index 1a4b037ae7272e5e1c1ffb1982149873f875f948..2a978aa96f4ba932e30f921e2e54bc40bad4b13e 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -205,6 +205,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -944,10 +944,10 @@ index 101f1a87a5fe920b57a5179da41cc91d88afa32e..b49dd636e730f0c5b609df68ee51bcd1 } // Paper end - Fix merchant inventory not closing on entity removal diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a13356b4ce56 100644 +index 6ea1381a607c40759ae422b7dcacebf65b810605..09500e88414928fc21aee48036f23b7265e95114 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -399,6 +399,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -392,6 +392,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent public @Nullable String clientBrandName = null; // Paper - Brand support public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event @@ -958,7 +958,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 // Paper start - rewrite chunk system private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -567,6 +571,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -560,6 +564,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc if (tag != null) { BlockPos.CODEC.parse(NbtOps.INSTANCE, tag).resultOrPartial(LOGGER::error).ifPresent(pos -> this.raidOmenPosition = pos); } @@ -969,7 +969,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 } @Override -@@ -611,6 +619,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -604,6 +612,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } this.saveEnderPearls(compound); @@ -979,7 +979,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 } private void saveParentVehicle(CompoundTag tag) { -@@ -843,6 +854,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -836,6 +847,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.trackEnteredOrExitedLavaOnVehicle(); this.updatePlayerAttributes(); this.advancements.flushDirty(this); @@ -995,7 +995,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 } private void updatePlayerAttributes() { -@@ -1130,6 +1150,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1123,6 +1143,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc ) ); Team team = this.getTeam(); @@ -1003,7 +1003,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) { this.server.getPlayerList().broadcastSystemMessage(deathMessage, false); } else if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { -@@ -1223,6 +1244,18 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1216,6 +1237,18 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc if (this.isInvulnerableTo(level, damageSource)) { return false; } else { @@ -1022,7 +1022,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 Entity entity = damageSource.getEntity(); if (!( // Paper - split the if statement. If below statement is false, hurtServer would not have been evaluated. Return false. !(entity instanceof Player player && !this.canHarmPlayer(player)) -@@ -1449,6 +1482,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1442,6 +1475,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc serverLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); this.unsetRemoved(); // CraftBukkit end @@ -1030,7 +1030,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 this.setServerLevel(level); this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); // CraftBukkit - use internal teleport without event this.connection.resetPosition(); -@@ -1566,7 +1600,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1559,7 +1593,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc new AABB(vec3.x() - 8.0, vec3.y() - 5.0, vec3.z() - 8.0, vec3.x() + 8.0, vec3.y() + 5.0, vec3.z() + 8.0), monster -> monster.isPreventingPlayerRest(this.serverLevel(), this) ); @@ -1039,7 +1039,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 return Either.left(Player.BedSleepingProblem.NOT_SAFE); } } -@@ -1603,7 +1637,19 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1596,7 +1630,19 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc CriteriaTriggers.SLEPT_IN_BED.trigger(this); }); if (!this.serverLevel().canSleepThroughNights()) { @@ -1060,7 +1060,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 } ((ServerLevel)this.level()).updateSleepingPlayerList(); -@@ -1711,6 +1757,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1704,6 +1750,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @Override public void openTextEdit(SignBlockEntity signEntity, boolean isFrontText) { @@ -1068,7 +1068,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 this.connection.send(new ClientboundBlockUpdatePacket(this.level(), signEntity.getBlockPos())); this.connection.send(new ClientboundOpenSignEditorPacket(signEntity.getBlockPos(), isFrontText)); } -@@ -2016,6 +2063,26 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2009,6 +2056,26 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.lastSentExp = -1; // CraftBukkit - Added to reset } @@ -1095,7 +1095,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 @Override public void displayClientMessage(Component chatComponent, boolean actionBar) { this.sendSystemMessage(chatComponent, actionBar); -@@ -2243,6 +2310,20 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2236,6 +2303,20 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc ); } @@ -1116,7 +1116,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 public void sendSystemMessage(Component mesage) { this.sendSystemMessage(mesage, false); } -@@ -2381,8 +2462,68 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2374,8 +2455,68 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc public void resetLastActionTime() { this.lastActionTime = Util.getMillis(); @@ -1185,7 +1185,7 @@ index 50db1221d672d36b58b65177a746d365f7cdc386..d4098a1666f0eb7060caaf93f5c5a133 public ServerStatsCounter getStats() { return this.stats; } -@@ -3086,4 +3227,56 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -3079,4 +3220,56 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return (org.bukkit.craftbukkit.entity.CraftPlayer) super.getBukkitEntity(); } // CraftBukkit end @@ -2341,7 +2341,7 @@ index efee812785240c1ab1fd47514cfb236a3548f9cf..b982d4b7bdf39fcaf5f22cc889467d7b protected ParticleOptions getInkParticle() { return ParticleTypes.GLOW_SQUID_INK; diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index 696ef08b2d897c91a20bc22987b1f5c7047615be..ac006d8738592bc5cb77033adc8c442ce302a476 100644 +index c068ce2034cffcf403091de16a43d54ba1290850..e66ad09280d8fb448953a6204d9fd81913227219 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -248,9 +248,9 @@ public abstract class LivingEntity extends Entity implements Attackable { @@ -3232,7 +3232,7 @@ index 6eaf0bd944349cd0c6084462ac385fa2caafe933..be59d0c27a83b329ec3f97c029cfb9c1 double d = this.llama.distanceToSqr(this.llama.getCaravanHead()); if (d > 676.0) { diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -index 579ca031d461ed4327fe4fb45c5289565322e64e..95fa516910a3834bbd4db6d11279e13a1f0dac41 100644 +index 005793fdee5ab03a0fb5a56b03b3db74e38448a1..c67a88c9c77ece7c85ffb169ac96da4f28291228 100644 --- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java @@ -35,7 +35,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal { @@ -13256,7 +13256,7 @@ index 5e0d447409dc2223bb56cb8bb932e241bf88c78d..6e1544121c556cd8761dc86d4246e727 } diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index acca8c51d2030c675c157b10d0bbc6af631afe61..e6419715fab462b12790ecb175ce1e1a1fceed8f 100644 +index 50af953a4698a3c6e16b840fab764dd733b3fbc9..3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -65,6 +65,7 @@ public abstract class AbstractContainerMenu { diff --git a/leaf-server/minecraft-patches/features/0019-Slice-Smooth-Teleports.patch b/leaf-server/minecraft-patches/features/0019-Slice-Smooth-Teleports.patch index d3df0b1e..fd8a1e3c 100644 --- a/leaf-server/minecraft-patches/features/0019-Slice-Smooth-Teleports.patch +++ b/leaf-server/minecraft-patches/features/0019-Slice-Smooth-Teleports.patch @@ -9,10 +9,10 @@ Original project: https://github.com/Cryptite/Slice Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 668c173dc69b4ab77d91666dc2059f2b9afd7ee7..d44c3baa2ef30d5cd4c46e491ff9198fa558513c 100644 +index 09500e88414928fc21aee48036f23b7265e95114..4160a1c6c063804f23c29c66231fa004bade3caa 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -403,6 +403,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -396,6 +396,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc private boolean tpsBar = false; // Purpur - Implement TPSBar private boolean compassBar = false; // Purpur - Add compass command private boolean ramBar = false; // Purpur - Implement rambar commands diff --git a/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch b/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch index f4f9c5a1..fc454c68 100644 --- a/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch +++ b/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch @@ -19,10 +19,10 @@ index ee7bdfd8f9da8d5989c9cc25f8cbcc94640361c5..8b9374ee6df71228bb8ea22661622a15 org.purpurmc.purpur.command.DemoCommand.register(this.dispatcher); // Purpur - Add demo command org.purpurmc.purpur.command.PingCommand.register(this.dispatcher); // Purpur - Add ping command diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 6ea52b077cd867528edcea2f8c5d1f925f2f304f..ab302d642b1d7b6f59b9ee32a0c514d0abf8d1b1 100644 +index 16a6aba187fa00fd7c3f739e46bc632987c1378f..8a9c2489730dd47cc776493695393e788fd2033e 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -2468,6 +2468,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2461,6 +2461,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc // Purpur start - AFK API private boolean isAfk = false; @@ -33,7 +33,7 @@ index 6ea52b077cd867528edcea2f8c5d1f925f2f304f..ab302d642b1d7b6f59b9ee32a0c514d0 @Override public void setAfk(boolean afk) { -@@ -2505,6 +2509,18 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2498,6 +2502,18 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, ""); String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, ""); if (afk) { diff --git a/leaf-server/minecraft-patches/features/0076-Fix-MC-119417.patch b/leaf-server/minecraft-patches/features/0076-Fix-MC-119417.patch index 68e9e9c0..2af29d1f 100644 --- a/leaf-server/minecraft-patches/features/0076-Fix-MC-119417.patch +++ b/leaf-server/minecraft-patches/features/0076-Fix-MC-119417.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Fix-MC-119417 Related MC issue: https://bugs.mojang.com/browse/MC-119417 diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index ab302d642b1d7b6f59b9ee32a0c514d0abf8d1b1..1957bd89e3ed34714c3633a27df63205a4b50b6b 100644 +index 8a9c2489730dd47cc776493695393e788fd2033e..4f01b53bf801f99253efd27df6216912705d18af 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -2268,6 +2268,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2261,6 +2261,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, gameMode.getId())); if (gameMode == GameType.SPECTATOR) { this.removeEntitiesOnShoulder(); diff --git a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch index 14389691..d0de99b1 100644 --- a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch +++ b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch @@ -33,18 +33,18 @@ index c1130f596cf3443eeb62eb1b12587172fe0859ee..18590e0b1d94ee3266637c5f3ab65ead @Override diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index e6419715fab462b12790ecb175ce1e1a1fceed8f..8a0d1aebad1f92c43112e279b9c5922fdd1fd432 100644 +index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..1e8a6b132926525fad405cbf3a2fab5d32e003e1 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -306,7 +306,12 @@ public abstract class AbstractContainerMenu { private void synchronizeCarriedToRemote() { if (!this.suppressRemoteUpdates) { -- if (!this.matchesRemote(this.getCarried(), this.remoteCarried)) { // Paper - add flag to simplify remote matching logic +- if (!ItemStack.matches(this.getCarried(), this.remoteCarried)) { + // Leaf start - Hide specified item components - Avoid some frequent client animations + final boolean matchResult = org.dreeam.leaf.config.modules.gameplay.HideItemComponent.enabled + ? !org.dreeam.leaf.util.item.ItemStackStripper.matchesStripped(this.getCarried(), this.remoteCarried) -+ : !this.matchesRemote(this.getCarried(), this.remoteCarried); // Paper - add flag to simplify remote matching logic ++ : ItemStack.matches(this.getCarried(), this.remoteCarried); // Paper - add flag to simplify remote matching logic + if (matchResult) { + // Leaf end - Hide specified item components - Avoid some frequent client animations this.remoteCarried = this.getCarried().copy(); diff --git a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch index 27419a08..aef1c4e6 100644 --- a/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch +++ b/leaf-server/minecraft-patches/features/0100-Smooth-teleport-config.patch @@ -9,10 +9,10 @@ happen but the visual "refresh" of a world change is hidden. Depending on the de this can act as a "smooth teleport" to a world if the new world is very similar looking to the old one. diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 1957bd89e3ed34714c3633a27df63205a4b50b6b..5ae1a69893cc9bee7126607e90fbaed1e9a9af06 100644 +index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424f68ba190 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1476,6 +1476,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1469,6 +1469,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc LevelData worlddata = level.getLevelData(); this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(level), (byte) 3)); @@ -20,7 +20,7 @@ index 1957bd89e3ed34714c3633a27df63205a4b50b6b..5ae1a69893cc9bee7126607e90fbaed1 this.connection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); PlayerList playerList = this.server.getPlayerList(); -@@ -1485,7 +1486,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1478,7 +1479,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc // CraftBukkit end this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur - Fix stuck in portals this.setServerLevel(level); diff --git a/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch b/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch index 0d13dc0c..cfe0faaf 100644 --- a/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch +++ b/leaf-server/minecraft-patches/features/0122-Cache-eligible-players-for-despawn-checks.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cache eligible players for despawn checks diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index a9e7424bb55266c5e04c56dcf598ce7d149eeb21..ae5d3de44fb710b48fdabf04f5e706df1f9889b7 100644 +index 61afe93ff7f6f6ac3967e948bf39b0ab559e2808..a66e5f6652d9633c856490de36d8d8fdf8a5298a 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -735,6 +735,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -38,10 +38,10 @@ index a9e7424bb55266c5e04c56dcf598ce7d149eeb21..ae5d3de44fb710b48fdabf04f5e706df .forEach( entity -> { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 5ae1a69893cc9bee7126607e90fbaed1e9a9af06..fefaab58da149b082a4d1e3bed9ec84ae8488d45 100644 +index 82a1715fea41e6a41c4ff441ea89f424f68ba190..8362def0dc61496a087bd859052bd80ebba83185 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1577,6 +1577,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1570,6 +1570,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.containerMenu.broadcastChanges(); } diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 6b316718..28e5f12a 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -568,7 +568,7 @@ index d4048661575ebfaf128ba25da365843774364e0e..33dd16a26edd2974f04d9a868d3e58e8 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index ae5d3de44fb710b48fdabf04f5e706df1f9889b7..31abf2da10bc9b4b7825ed4b3d4e9da52feb2e39 100644 +index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d08e18122 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -182,7 +182,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -650,10 +650,10 @@ index ae5d3de44fb710b48fdabf04f5e706df1f9889b7..31abf2da10bc9b4b7825ed4b3d4e9da5 // Paper start - extra debug info if (entity.valid) { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcefccb3eb3c 100644 +index 8362def0dc61496a087bd859052bd80ebba83185..09f517059aa47ca67329bc913243d4fdee09abe5 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -434,6 +434,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -427,6 +427,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return this.viewDistanceHolder; } // Paper end - rewrite chunk system @@ -661,7 +661,7 @@ index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcef public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) { super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile); -@@ -810,6 +811,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -803,6 +804,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @Override public void tick() { @@ -669,7 +669,7 @@ index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcef // CraftBukkit start if (this.joining) { this.joining = false; -@@ -1455,6 +1457,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1448,6 +1450,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc teleportTransition.postTeleportTransition().onTransition(this); return this; } else { @@ -678,7 +678,7 @@ index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcef // CraftBukkit start /* this.isChangingDimension = true; -@@ -1826,6 +1830,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1819,6 +1823,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return OptionalInt.empty(); } else { // CraftBukkit start @@ -691,7 +691,7 @@ index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcef this.containerMenu = abstractContainerMenu; // Moved up if (!this.isImmobile()) this.connection -@@ -1890,6 +1900,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1883,6 +1893,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } @Override public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { @@ -1015,7 +1015,7 @@ index d212f57c8c0b2086f567fd30237b110203d9e8cb..ed4df82581b5411e54068ccc59ea85a7 } else { Entity entity = owner.teleport( diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 8a0d1aebad1f92c43112e279b9c5922fdd1fd432..d4fc9466d61a680b85859965a8f7dc795b8c7130 100644 +index 1e8a6b132926525fad405cbf3a2fab5d32e003e1..ea3ddc712676b09298f821fac4145a164bccd7c7 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -92,8 +92,14 @@ public abstract class AbstractContainerMenu { diff --git a/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch index 4a11921d..b322df76 100644 --- a/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch @@ -34,7 +34,7 @@ index 98af1ad020a003db66d7319f33d43deec315aec5..e04a6db55d936277f2a852374f11d483 for (int i = 0; i < this.tickables.size(); i++) { this.tickables.get(i).run(); diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index a13ce614722d26dcbb2f7f3ceeb68915bbaec304..7f91637028c0f3a45b32e831f88e97185bfdbdc8 100644 +index 38314fcb660f6cbb36d60434d24df5b579425eb3..f17980536b98f20443556ed294c970bc2d570bbb 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -283,6 +283,7 @@ public class ServerEntity { @@ -46,10 +46,10 @@ index a13ce614722d26dcbb2f7f3ceeb68915bbaec304..7f91637028c0f3a45b32e831f88e9718 // Purpur start diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 9100da3fe4e478cea7198cb4e028fcefccb3eb3c..95e03045bcbee74ddac36ae36ce8c8c2f5769fa4 100644 +index 09f517059aa47ca67329bc913243d4fdee09abe5..43c4b8e54842310e48bcdaa991c68ff9571d7249 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -866,6 +866,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -859,6 +859,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } } // Purpur end - Ridables diff --git a/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch b/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch deleted file mode 100644 index 198d4cdd..00000000 --- a/leaf-server/minecraft-patches/features/0166-Save-world-async-properly.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Thu, 8 May 2025 00:05:01 +0200 -Subject: [PATCH] Save world async properly - -P.S from Tai: I've been using this fix for weeks in my own server but didn't had balls to push it as thought it may cause issues but, it's merged in paper 1.21.5 now. - -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index ba1dd51e7187a80e8438e46383257c22f5382130..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -1439,7 +1439,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - - if (doFull) { -- this.saveLevelData(true); -+ this.saveLevelData(false); - } - // chunk autosave is already called by the ChunkSystem during unload processing (ChunkMap#processUnloads) - // Copied from save() diff --git a/leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0166-reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0167-reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0166-reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch index a0894663..01cbb5fe 100644 --- a/leaf-server/minecraft-patches/features/0168-async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch @@ -119,7 +119,7 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..c70d5a0db1dfd01eab323aefd07d6e81 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index e9ce273812259627b61824ca4ffe83d301a4d946..d7c113706d94ea510ddf7d0fffa927a15b198e9a 100644 +index 49f1743db193be1f10bfe6419231eb682e1068f7..b0ffd2077747b2325ab795eef457b9a0fa44754b 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java @@ -472,11 +472,32 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, diff --git a/leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0168-Optimise-BlockEntities-tickersInLevel.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0169-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0168-Optimise-BlockEntities-tickersInLevel.patch diff --git a/leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0170-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0170-flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0171-flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0170-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch similarity index 93% rename from leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch index 93a52c64..a2b04b38 100644 --- a/leaf-server/minecraft-patches/features/0172-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Only tick items at hand diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 95e03045bcbee74ddac36ae36ce8c8c2f5769fa4..6eee16dccef1d0f04ba3532f5ee064478b842425 100644 +index 43c4b8e54842310e48bcdaa991c68ff9571d7249..943f48c41214f440517ecf7f392e08f0e4d1b888 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -895,9 +895,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -888,9 +888,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc super.tick(); } diff --git a/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch diff --git a/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch b/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch new file mode 100644 index 00000000..0cab23f7 --- /dev/null +++ b/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +@@ -185,6 +_,8 @@ + public CompressionFormat compressionFormat = CompressionFormat.ZLIB; + @Comment("This setting controls if equipment should be updated when handling certain player actions.") + public boolean updateEquipmentOnPlayerActions = true; ++ @Comment("Only checks an item's amount and type instead of its full data during inventory desync checks.") ++ public boolean simplifyRemoteItemMatching = false; + + public enum CompressionFormat { + GZIP, diff --git a/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch b/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch new file mode 100644 index 00000000..d96016a3 --- /dev/null +++ b/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch @@ -0,0 +1,32 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -221,6 +_,7 @@ + private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener(); + public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API + private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit ++ private boolean simplifyContainerDesyncCheck = GlobalConfiguration.get().unsupportedSettings.simplifyRemoteItemMatching; + private long lastSaveTime; // Paper - getLastPlayed replacement API + + public CraftPlayer(CraftServer server, ServerPlayer entity) { +@@ -3601,5 +_,21 @@ + @Override + public void setDeathScreenScore(final int score) { + getHandle().setScore(score); ++ } ++ ++ /** ++ * Returns whether container desync checks should skip the full item comparison of remote carried and changed slots ++ * and should instead only check their type and amount. ++ *

++ * This is useful if the client is not able to produce the same item stack (or as of 1.21.5, its data hashes) as the server. ++ * ++ * @return whether to simplify container desync checks ++ */ ++ public boolean simplifyContainerDesyncCheck() { ++ return simplifyContainerDesyncCheck; ++ } ++ ++ public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) { ++ this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck; + } + } From 189ac58976bac7eb7322138cdd832200ed9962a5 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 12 May 2025 22:13:03 +0200 Subject: [PATCH 49/81] remove couple streams --- .../0174-Remove-streams-in-MobSensor.patch | 26 +++++++++ ...175-Remove-streams-in-TemptingSensor.patch | 55 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch create mode 100644 leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch new file mode 100644 index 00000000..40643cb2 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Mon, 12 May 2025 19:11:16 +0200 +Subject: [PATCH] Remove streams in MobSensor + + +diff --git a/net/minecraft/world/entity/ai/sensing/MobSensor.java b/net/minecraft/world/entity/ai/sensing/MobSensor.java +index bda210b4809a5aade7ab4d0f26fdda4d5f53f619..18c7fe0b39b366d3ac4fd83415953c063e5c562f 100644 +--- a/net/minecraft/world/entity/ai/sensing/MobSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/MobSensor.java +@@ -40,7 +40,14 @@ public class MobSensor extends Sensor { + public void checkForMobsNearby(T sensingEntity) { + Optional> memory = sensingEntity.getBrain().getMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES); + if (!memory.isEmpty()) { +- boolean flag = memory.get().stream().anyMatch(livingEntity -> this.mobTest.test(sensingEntity, livingEntity)); ++ boolean flag = false; ++ List entities = memory.get(); ++ for (LivingEntity livingEntity : entities) { ++ if (this.mobTest.test(sensingEntity, livingEntity)) { ++ flag = true; ++ break; ++ } ++ } + if (flag) { + this.mobDetected(sensingEntity); + } diff --git a/leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch new file mode 100644 index 00000000..f6ec76a7 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Mon, 12 May 2025 19:39:03 +0200 +Subject: [PATCH] Remove streams in TemptingSensor + + +diff --git a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java +index 4b3ba795bc18417f983600f1edbc1895ccb7deab..48fef89a7a3286c17a56a842fa44b80797971d2f 100644 +--- a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java +@@ -1,12 +1,13 @@ + package net.minecraft.world.entity.ai.sensing; + + import com.google.common.collect.ImmutableSet; ++import java.util.ArrayList; + import java.util.Comparator; + import java.util.List; + import java.util.Set; + import java.util.function.Predicate; +-import java.util.stream.Collectors; + import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.ServerPlayer; + import net.minecraft.world.entity.EntitySelector; + import net.minecraft.world.entity.PathfinderMob; + import net.minecraft.world.entity.ai.Brain; +@@ -36,14 +37,21 @@ public class TemptingSensor extends Sensor { + protected void doTick(ServerLevel level, PathfinderMob entity) { + Brain brain = entity.getBrain(); + TargetingConditions targetingConditions = TEMPT_TARGETING.copy().range((float)entity.getAttributeValue(Attributes.TEMPT_RANGE)); +- List list = level.players() +- .stream() +- .filter(EntitySelector.NO_SPECTATORS) +- .filter(serverPlayer -> targetingConditions.test(level, entity, serverPlayer)) +- .filter(this::playerHoldingTemptation) +- .filter(serverPlayer -> !entity.hasPassenger(serverPlayer)) +- .sorted(Comparator.comparingDouble(entity::distanceToSqr)) +- .collect(Collectors.toList()); ++ ++ List allPlayers = level.players(); ++ List list = new ArrayList<>(); ++ ++ for (Player serverPlayer : allPlayers) { ++ if (EntitySelector.NO_SPECTATORS.test(serverPlayer) && ++ targetingConditions.test(level, entity, serverPlayer) && ++ this.playerHoldingTemptation(serverPlayer) && ++ !entity.hasPassenger(serverPlayer)) { ++ list.add(serverPlayer); ++ } ++ } ++ ++ list.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ + if (!list.isEmpty()) { + Player player = list.get(0); + // CraftBukkit start From e01facb2a37bf4efab900f7adbb53c0550d67e0b Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 13 May 2025 05:20:20 -0400 Subject: [PATCH 50/81] Update Configurable unknown command message patch to follow the vanilla * Add to be more configurable * Fix for custom brigadier exception message #319 --- ...Configurable-unknown-command-message.patch | 92 ++++++++++--------- ...yList-implementation-to-BasicEntity.patch} | 0 ...heck-inside-blocks-and-traverse-blo.patch} | 6 +- ...ol-Core.patch => 0164-Protocol-Core.patch} | 0 ... => 0165-reduce-PlayerChunk-Updates.patch} | 0 ... 0166-async-switch-connection-state.patch} | 2 +- ...timise-BlockEntities-tickersInLevel.patch} | 0 ...e-cactus-can-even-survive-being-pla.patch} | 0 ...0169-flush-location-while-knockback.patch} | 0 ...tch => 0170-Only-tick-items-at-hand.patch} | 0 ...art-sort-items-in-NearestItemSensor.patch} | 0 ...172-Optimise-player-movement-checks.patch} | 4 +- ...=> 0173-Remove-streams-in-MobSensor.patch} | 0 ...74-Remove-streams-in-TemptingSensor.patch} | 0 ...tion.patch => 0175-paw-optimization.patch} | 12 +-- .../modules/misc/UnknownCommandMessage.java | 10 +- 16 files changed, 68 insertions(+), 58 deletions(-) rename leaf-server/minecraft-patches/features/{0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch => 0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch} (100%) rename leaf-server/minecraft-patches/features/{0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch => 0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch} (94%) rename leaf-server/minecraft-patches/features/{0165-Protocol-Core.patch => 0164-Protocol-Core.patch} (100%) rename leaf-server/minecraft-patches/features/{0166-reduce-PlayerChunk-Updates.patch => 0165-reduce-PlayerChunk-Updates.patch} (100%) rename leaf-server/minecraft-patches/features/{0167-async-switch-connection-state.patch => 0166-async-switch-connection-state.patch} (99%) rename leaf-server/minecraft-patches/features/{0168-Optimise-BlockEntities-tickersInLevel.patch => 0167-Optimise-BlockEntities-tickersInLevel.patch} (100%) rename leaf-server/minecraft-patches/features/{0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch => 0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch} (100%) rename leaf-server/minecraft-patches/features/{0170-flush-location-while-knockback.patch => 0169-flush-location-while-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0171-Only-tick-items-at-hand.patch => 0170-Only-tick-items-at-hand.patch} (100%) rename leaf-server/minecraft-patches/features/{0172-Smart-sort-items-in-NearestItemSensor.patch => 0171-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0173-Optimise-player-movement-checks.patch => 0172-Optimise-player-movement-checks.patch} (84%) rename leaf-server/minecraft-patches/features/{0174-Remove-streams-in-MobSensor.patch => 0173-Remove-streams-in-MobSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0175-Remove-streams-in-TemptingSensor.patch => 0174-Remove-streams-in-TemptingSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0162-paw-optimization.patch => 0175-paw-optimization.patch} (95%) diff --git a/leaf-server/minecraft-patches/features/0055-Configurable-unknown-command-message.patch b/leaf-server/minecraft-patches/features/0055-Configurable-unknown-command-message.patch index 24d85d98..3339f433 100644 --- a/leaf-server/minecraft-patches/features/0055-Configurable-unknown-command-message.patch +++ b/leaf-server/minecraft-patches/features/0055-Configurable-unknown-command-message.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable unknown command message diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java -index fb18f69cb26132fc8c53b185454c6aadb8a5f7e5..eff6d524c8acfc62d1fcf6b5552754e794a22735 100644 +index 8b9374ee6df71228bb8ea22661622a15cf3bc350..59e5f93df54abc56329b38340882dade7d7104a3 100644 --- a/net/minecraft/commands/Commands.java +++ b/net/minecraft/commands/Commands.java @@ -404,31 +404,9 @@ public class Commands { @@ -13,7 +13,7 @@ index fb18f69cb26132fc8c53b185454c6aadb8a5f7e5..eff6d524c8acfc62d1fcf6b5552754e7 final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); // source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); - builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(var7.getRawMessage())); -+ final net.kyori.adventure.text.TextComponent message = getUnknownCommandMessage(builder, var7, label); // Leaf - Configurable unknown command message ++ final net.kyori.adventure.text.Component message = getUnknownCommandMessage(builder, var7, label); // Leaf - Configurable unknown command message // Paper end - Add UnknownCommandEvent - if (var7.getInput() != null && var7.getCursor() >= 0) { - int min = Math.min(var7.getInput().length(), var7.getCursor()); @@ -42,59 +42,65 @@ index fb18f69cb26132fc8c53b185454c6aadb8a5f7e5..eff6d524c8acfc62d1fcf6b5552754e7 org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); if (event.message() != null) { source.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); -@@ -680,6 +658,86 @@ public class Commands { +@@ -680,6 +658,92 @@ public class Commands { }; } + // Leaf start - Configurable unknown command message -+ private static net.kyori.adventure.text.TextComponent getUnknownCommandMessage( ++ private static net.kyori.adventure.text.Component getUnknownCommandMessage( + net.kyori.adventure.text.TextComponent.Builder builder, CommandSyntaxException commandSyntaxException, String label + ) { + String rawMessage = org.dreeam.leaf.config.modules.misc.UnknownCommandMessage.unknownCommandMessage; + -+ if (!"default".equals(rawMessage)) { -+ final String input = commandSyntaxException.getInput(); -+ final int cursor = commandSyntaxException.getCursor(); -+ -+ if (rawMessage.contains("") && input != null && cursor >= 0) { -+ final int min = Math.min(input.length(), cursor); -+ final net.kyori.adventure.text.TextComponent.Builder detail = net.kyori.adventure.text.Component.text(); -+ final net.kyori.adventure.text.Component context = net.kyori.adventure.text.Component.translatable("command.context.here") -+ .color(net.kyori.adventure.text.format.NamedTextColor.RED) -+ .decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC); -+ final net.kyori.adventure.text.event.ClickEvent event = net.kyori.adventure.text.event.ClickEvent.suggestCommand("/" + label); -+ -+ detail.color(net.kyori.adventure.text.format.NamedTextColor.GRAY); -+ -+ if (min > 10) { -+ detail.append(net.kyori.adventure.text.Component.text("...")); -+ } -+ -+ detail.append(net.kyori.adventure.text.Component.text(input.substring(Math.max(0, min - 10), min))); -+ if (min < input.length()) { -+ net.kyori.adventure.text.Component commandInput = net.kyori.adventure.text.Component.text(input.substring(min)) -+ .color(net.kyori.adventure.text.format.NamedTextColor.RED) -+ .decorate(net.kyori.adventure.text.format.TextDecoration.UNDERLINED); -+ -+ detail.append(commandInput); -+ } -+ -+ detail.append(context); -+ detail.clickEvent(event); -+ -+ builder.append(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(rawMessage, net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("detail", detail.build()))); -+ } else { -+ rawMessage = rawMessage.replace("", ""); -+ builder.append(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(rawMessage)); -+ } -+ -+ return builder.build(); ++ if ("default".equals(rawMessage)) { ++ return getVanillaUnknownCommandMessage(builder, commandSyntaxException, label); + } + -+ return getVanillaUnknownCommandMessage(builder, commandSyntaxException, label); ++ net.kyori.adventure.text.Component messageComponent = null; ++ net.kyori.adventure.text.Component detailComponent = null; ++ ++ if (rawMessage.contains("")) { ++ messageComponent = io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(commandSyntaxException.getRawMessage()); ++ } ++ ++ final String input = commandSyntaxException.getInput(); ++ final int cursor = commandSyntaxException.getCursor(); ++ ++ if (rawMessage.contains("") && input != null && cursor >= 0) { ++ final int min = Math.min(input.length(), cursor); ++ final net.kyori.adventure.text.TextComponent.Builder detail = net.kyori.adventure.text.Component.text(); ++ final net.kyori.adventure.text.Component context = net.kyori.adventure.text.Component.translatable("command.context.here") ++ .color(net.kyori.adventure.text.format.NamedTextColor.RED) ++ .decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC); ++ final net.kyori.adventure.text.event.ClickEvent event = net.kyori.adventure.text.event.ClickEvent.suggestCommand("/" + label); ++ ++ detail.color(net.kyori.adventure.text.format.NamedTextColor.GRAY); ++ ++ if (min > 10) { ++ detail.append(net.kyori.adventure.text.Component.text("...")); ++ } ++ ++ detail.append(net.kyori.adventure.text.Component.text(input.substring(Math.max(0, min - 10), min))); ++ if (min < input.length()) { ++ net.kyori.adventure.text.Component commandInput = net.kyori.adventure.text.Component.text(input.substring(min)) ++ .color(net.kyori.adventure.text.format.NamedTextColor.RED) ++ .decorate(net.kyori.adventure.text.format.TextDecoration.UNDERLINED); ++ ++ detail.append(commandInput); ++ } ++ ++ detail.append(context); ++ detail.clickEvent(event); ++ ++ detailComponent = detail.build(); ++ } ++ ++ return builder.append(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(rawMessage)).build() ++ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(messageComponent).build()) ++ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("").replacement(detailComponent).build()); + } + -+ private static net.kyori.adventure.text.TextComponent getVanillaUnknownCommandMessage( ++ private static net.kyori.adventure.text.Component getVanillaUnknownCommandMessage( + net.kyori.adventure.text.TextComponent.Builder builder, CommandSyntaxException var7, String label + ) { + builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(var7.getRawMessage())); diff --git a/leaf-server/minecraft-patches/features/0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch diff --git a/leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 94% rename from leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch index e3ef4896..c678193f 100644 --- a/leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch +++ b/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Sakura: Optimise-check-inside-blocks-and-traverse-blocks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 4221e5322fa3a3ff6ab53946aa71d54144d2c4b2..4474639b0411236e208c542973084864365c544c 100644 +index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..ad674cf202c61b50f568a36777cee196627b402c 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1670,6 +1670,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1711,6 +1711,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private void checkInsideBlocks(List movements, Set blocksInside) { if (this.isAffectedByBlocks()) { LongSet set = this.visitedBlocks; @@ -20,7 +20,7 @@ index 4221e5322fa3a3ff6ab53946aa71d54144d2c4b2..4474639b0411236e208c542973084864 for (Entity.Movement movement : movements) { Vec3 vec3 = movement.from(); -@@ -1681,7 +1686,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1722,7 +1727,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return; } diff --git a/leaf-server/minecraft-patches/features/0165-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0165-Protocol-Core.patch rename to leaf-server/minecraft-patches/features/0164-Protocol-Core.patch diff --git a/leaf-server/minecraft-patches/features/0166-reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0165-reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0166-reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0165-reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0166-async-switch-connection-state.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0166-async-switch-connection-state.patch index 01cbb5fe..03b4f3b5 100644 --- a/leaf-server/minecraft-patches/features/0167-async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0166-async-switch-connection-state.patch @@ -5,7 +5,7 @@ Subject: [PATCH] async switch connection state diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f998cf8d70302a21289de4d84b46d322d0b8a8fe..32f26640664135c9f7f45f8b204b7ff412fe343e 100644 +index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3a82e1e510029576485427af9fd705b37c5f6e20 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -342,6 +342,11 @@ public class Connection extends SimpleChannelInboundHandler> { diff --git a/leaf-server/minecraft-patches/features/0168-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0167-Optimise-BlockEntities-tickersInLevel.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0168-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0167-Optimise-BlockEntities-tickersInLevel.patch diff --git a/leaf-server/minecraft-patches/features/0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0169-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0170-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0169-flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0170-flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0169-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0170-Only-tick-items-at-hand.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0171-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0170-Only-tick-items-at-hand.patch diff --git a/leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0172-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch similarity index 84% rename from leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch index aad1cc16..c7f53dc0 100644 --- a/leaf-server/minecraft-patches/features/0173-Optimise-player-movement-checks.patch +++ b/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise player movement checks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 2786fbc43f238be058b153ba551f0a616c4a18f4..376d8cb183145955b887597ef7ae25ce761a795a 100644 +index ad674cf202c61b50f568a36777cee196627b402c..5a31ac325af5727a042e4056f67f53013cd33c39 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1176,7 +1176,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1210,7 +1210,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end diff --git a/leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0173-Remove-streams-in-MobSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Remove-streams-in-MobSensor.patch rename to leaf-server/minecraft-patches/features/0173-Remove-streams-in-MobSensor.patch diff --git a/leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0174-Remove-streams-in-TemptingSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0175-Remove-streams-in-TemptingSensor.patch rename to leaf-server/minecraft-patches/features/0174-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch b/leaf-server/minecraft-patches/features/0175-paw-optimization.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0162-paw-optimization.patch rename to leaf-server/minecraft-patches/features/0175-paw-optimization.patch index d2e6630c..cb9142a0 100644 --- a/leaf-server/minecraft-patches/features/0162-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0175-paw-optimization.patch @@ -10,10 +10,10 @@ Some random optimizations - Secret patches (WIP) diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..f998cf8d70302a21289de4d84b46d322d0b8a8fe 100644 +index 3a82e1e510029576485427af9fd705b37c5f6e20..32f26640664135c9f7f45f8b204b7ff412fe343e 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -617,13 +617,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -661,13 +661,7 @@ public class Connection extends SimpleChannelInboundHandler> { if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { @@ -94,7 +94,7 @@ index 0860a700106e8c1afe58c77150a0f3aee8393fdd..8e5414becccc921db39e4e6eebeb054d this.tickChunks(l, list); // Gale - Purpur - remove vanilla profiler } finally { diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index fbfb35dad8b07c31f967d33fb04cfcfc94557d72..ba1dd51e7187a80e8438e46383257c22f5382130 100644 +index 539206d55e87ec9968664305caaf475c991fe4d5..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1387,13 +1387,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -126,7 +126,7 @@ index fbfb35dad8b07c31f967d33fb04cfcfc94557d72..ba1dd51e7187a80e8438e46383257c22 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 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..124624d9c8cc292fcedb5652542b7d9da4e5d228 100644 +index 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce761a795a 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -1143,31 +1143,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -192,7 +192,7 @@ index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..124624d9c8cc292fcedb5652542b7d9d } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4832,9 +4791,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4849,9 +4808,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -202,7 +202,7 @@ index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..124624d9c8cc292fcedb5652542b7d9d } public void addDeltaMovement(Vec3 addend) { -@@ -4940,9 +4897,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4957,9 +4914,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix MC-4 if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/UnknownCommandMessage.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/UnknownCommandMessage.java index 8c20b8df..d659fbb0 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/UnknownCommandMessage.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/misc/UnknownCommandMessage.java @@ -9,15 +9,19 @@ public class UnknownCommandMessage extends ConfigModules { return EnumConfigCategory.MISC.getBaseKeyName() + ".message"; } - public static String unknownCommandMessage = ""; + public static String unknownCommandMessage = "default"; @Override public void onLoaded() { unknownCommandMessage = config.getString(getBasePath() + ".unknown-command", unknownCommandMessage, config.pickStringRegionBased(""" Unknown command message, using MiniMessage format, set to "default" to use vanilla message, - placeholder: , shows detail of the unknown command information.""", + placeholder: + , show message of the command exception. + , shows detail of the command exception.""", """ 发送未知命令时的消息, 使用 MiniMessage 格式, 设置为 "default" 使用原版消息. - 变量: , 显示未知命令详细信息.""")); + 变量: + , 显示命令错误所附提示消息. + , 显示命令错误详细信息.""")); } } From 0bdfeeb528b23c1378cf87731f457c15153fe115 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 13 May 2025 19:59:05 +0900 Subject: [PATCH 51/81] temp disable async-target-finding --- .../features/0154-Async-target-finding.patch | 56 +------------------ ...erPR-Fix-save-load-NaN-Entity-Motion.patch | 6 +- ...check-inside-blocks-and-traverse-blo.patch | 6 +- ...0172-Optimise-player-movement-checks.patch | 4 +- .../features/0175-paw-optimization.patch | 12 ++-- .../modules/async/AsyncTargetFinding.java | 2 + 6 files changed, 17 insertions(+), 69 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index d41c09d8..f8dacc8d 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -48,7 +48,7 @@ index 33dd16a26edd2974f04d9a868d3e58e8e3060032..eb0589b203bcf72cd24bb37f2c448c23 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 90bdcd168ad5b1a940f81b191bd59a34d3a33070..fbfb35dad8b07c31f967d33fb04cfcfc94557d72 100644 +index 9af7dafe03812d96aa477584d4147a68c240ab21..539206d55e87ec9968664305caaf475c991fe4d5 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -177,7 +177,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -135,60 +135,6 @@ index 90bdcd168ad5b1a940f81b191bd59a34d3a33070..fbfb35dad8b07c31f967d33fb04cfcfc } // Paper start - log detailed entity tick information -diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 0903508d2cd3c78602e62dbcff4aa70285bc4c4f..e99fd55a90c68cc96701e5291219d1d056ed266b 100644 ---- a/net/minecraft/world/entity/Entity.java -+++ b/net/minecraft/world/entity/Entity.java -@@ -243,6 +243,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - protected Vec3 stuckSpeedMultiplier = Vec3.ZERO; - @Nullable - private Entity.RemovalReason removalReason; -+ private volatile boolean isRemoved = false; // Leaf - Async target finding - volatile removal check - public static final float DEFAULT_BB_WIDTH = 0.6F; - public static final float DEFAULT_BB_HEIGHT = 1.8F; - public float moveDist; -@@ -5035,7 +5036,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - public final boolean isRemoved() { -- return this.removalReason != null; -+ // Leaf start - Async target finding -+ // Volatile removal check -+ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ return this.isRemoved; -+ } else { -+ return this.removalReason != null; -+ } -+ // Leaf end - Async target finding - } - - @Nullable -@@ -5062,6 +5070,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers - if (this.removalReason == null) { - this.removalReason = removalReason; -+ // Leaf start - Async target finding -+ // Volatile removal check -+ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled && this.removalReason != null) { -+ this.isRemoved = true; -+ } -+ // Leaf end - Async target finding - } - - if (this.removalReason.shouldDestroy()) { -@@ -5081,6 +5095,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - - public void unsetRemoved() { - this.removalReason = null; -+ // Leaf start - Async target finding -+ // Volatile removal check -+ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ this.isRemoved = false; -+ } -+ // Leaf end - Async target finding - } - - // Paper start - Folia schedulers diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff339241705d63 100644 --- a/net/minecraft/world/entity/Mob.java diff --git a/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch b/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch index a2db5aa6..3725ae0e 100644 --- a/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch +++ b/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch @@ -10,10 +10,10 @@ Paper pull request: https://github.com/PaperMC/Paper/pull/12269 Fix Paper#12262 using like the same logic than "pitch/yaw" for set to 0 when a value is NaN diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index f114a8f5143799d72e36e0a535888c5fb25213e1..a81983182ee3e3b874ba83ddf9bbc6ea772a2997 100644 +index 0903508d2cd3c78602e62dbcff4aa70285bc4c4f..df4b26a6304df92f4eda27ac986ca78d5217ce6c 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -2474,6 +2474,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2473,6 +2473,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // CraftBukkit end @@ -21,7 +21,7 @@ index f114a8f5143799d72e36e0a535888c5fb25213e1..a81983182ee3e3b874ba83ddf9bbc6ea Vec3 deltaMovement = this.getDeltaMovement(); compound.put("Motion", this.newDoubleList(deltaMovement.x, deltaMovement.y, deltaMovement.z)); // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero -@@ -2620,9 +2621,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2619,9 +2620,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess ListTag list = compound.getList("Pos", 6); ListTag list1 = compound.getList("Motion", 6); ListTag list2 = compound.getList("Rotation", 5); diff --git a/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch index c678193f..cc4289ed 100644 --- a/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch +++ b/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Sakura: Optimise-check-inside-blocks-and-traverse-blocks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..ad674cf202c61b50f568a36777cee196627b402c 100644 +index df4b26a6304df92f4eda27ac986ca78d5217ce6c..a4de10a32f49b7b361fc9dd1d142caeef8d7d148 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1711,6 +1711,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1710,6 +1710,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private void checkInsideBlocks(List movements, Set blocksInside) { if (this.isAffectedByBlocks()) { LongSet set = this.visitedBlocks; @@ -20,7 +20,7 @@ index 2c15f8f0be8ba03f2c9481bed0d46aad738848a0..ad674cf202c61b50f568a36777cee196 for (Entity.Movement movement : movements) { Vec3 vec3 = movement.from(); -@@ -1722,7 +1727,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1721,7 +1726,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return; } diff --git a/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch index c7f53dc0..0a2e4e7e 100644 --- a/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch +++ b/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimise player movement checks diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index ad674cf202c61b50f568a36777cee196627b402c..5a31ac325af5727a042e4056f67f53013cd33c39 100644 +index a4de10a32f49b7b361fc9dd1d142caeef8d7d148..5eb740476dff894e91c2bd779ef6b760213a78a5 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1210,7 +1210,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1209,7 +1209,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end diff --git a/leaf-server/minecraft-patches/features/0175-paw-optimization.patch b/leaf-server/minecraft-patches/features/0175-paw-optimization.patch index cb9142a0..d3301e11 100644 --- a/leaf-server/minecraft-patches/features/0175-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0175-paw-optimization.patch @@ -126,10 +126,10 @@ index 539206d55e87ec9968664305caaf475c991fe4d5..6cb0c14cb7aa243bbee6ca9ba57da4cc 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 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce761a795a 100644 +index 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5178e070c 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1143,31 +1143,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1142,31 +1142,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.onGround; } @@ -161,7 +161,7 @@ index 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce public void move(MoverType type, Vec3 movement) { // Gale start - VMP - skip entity move if movement is zero if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { -@@ -1175,16 +1150,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1174,16 +1149,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Gale end - VMP - skip entity move if movement is zero final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity @@ -178,7 +178,7 @@ index 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { -@@ -1305,13 +1271,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1304,13 +1270,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Gale end - skip negligible planar movement multiplication } } @@ -192,7 +192,7 @@ index 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4849,9 +4808,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4848,9 +4807,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -202,7 +202,7 @@ index 5a31ac325af5727a042e4056f67f53013cd33c39..376d8cb183145955b887597ef7ae25ce } public void addDeltaMovement(Vec3 addend) { -@@ -4957,9 +4914,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4956,9 +4913,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix MC-4 if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index adf14fd7..479eb133 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -34,6 +34,8 @@ public class AsyncTargetFinding extends ConfigModules { asyncTargetFindingInitialized = true; enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + // TODO + 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); From 63edc90be8be406876936e9cc5334474cb88e68d Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 14 May 2025 00:41:32 +0900 Subject: [PATCH 52/81] fix async-target-finding --- .../features/0154-Async-target-finding.patch | 679 ++++++++++-------- ...tyList-implementation-to-BasicEntity.patch | 10 +- .../leaf/async/ai/AsyncGoalExecutor.java | 84 ++- .../java/org/dreeam/leaf/async/ai/VWaker.java | 8 + .../java/org/dreeam/leaf/async/ai/Waker.java | 16 +- .../modules/async/AsyncTargetFinding.java | 4 +- 6 files changed, 448 insertions(+), 353 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index f8dacc8d..5287746c 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -4,6 +4,105 @@ Date: Sat, 29 Mar 2025 13:40:46 +0100 Subject: [PATCH] Async target finding +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..635d6aabbbe9be5c81a71d5e5bf758211deec0cf 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +@@ -40,8 +40,8 @@ public final class ChunkEntitySlices { + + private final EntityCollectionBySection allEntities; + private final EntityCollectionBySection hardCollidingEntities; +- private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; +- private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByType; ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; // Leaf ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByType; // Leaf + private final EntityList entities = new EntityList(); + + public FullChunkStatus status; +@@ -67,9 +67,16 @@ public final class ChunkEntitySlices { + + this.allEntities = new EntityCollectionBySection(this); + this.hardCollidingEntities = new EntityCollectionBySection(this); +- this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); +- this.entitiesByType = new Reference2ObjectOpenHashMap<>(); + ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ this.entitiesByClass = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); ++ this.entitiesByType = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); ++ } else { ++ this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); ++ this.entitiesByType = new Reference2ObjectOpenHashMap<>(); ++ } ++ // Leaf end + this.status = status; + this.chunkData = chunkData; + } +@@ -248,14 +255,26 @@ public final class ChunkEntitySlices { + this.hardCollidingEntities.addEntity(entity, sectionIndex); + } + +- for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ synchronized (this.entitiesByClass) { ++ for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().addEntity(entity, sectionIndex); ++ } ++ } ++ } ++ } else { ++ for (final Iterator, EntityCollectionBySection>> iterator = ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext(); ) { ++ final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + +- if (entry.getKey().isInstance(entity)) { +- entry.getValue().addEntity(entity, sectionIndex); ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().addEntity(entity, sectionIndex); ++ } + } + } ++ // Leaf end + + EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); + if (byType != null) { +@@ -282,14 +301,27 @@ public final class ChunkEntitySlices { + this.hardCollidingEntities.removeEntity(entity, sectionIndex); + } + +- for (final Iterator, EntityCollectionBySection>> iterator = +- this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { +- final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); ++ // Leaf start ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ synchronized (this.entitiesByClass) { ++ for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().removeEntity(entity, sectionIndex); ++ } ++ } ++ } ++ } else { ++ for (final Iterator, EntityCollectionBySection>> iterator = ++ this.entitiesByClass.reference2ObjectEntrySet().iterator(); iterator.hasNext();) { ++ final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); + +- if (entry.getKey().isInstance(entity)) { +- entry.getValue().removeEntity(entity, sectionIndex); ++ if (entry.getKey().isInstance(entity)) { ++ entry.getValue().removeEntity(entity, sectionIndex); ++ } + } + } ++ // Leaf end ++ + + final EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); + byType.removeEntity(entity, sectionIndex); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 24926aa7ed5c78b235659daf18b224b14beb744c..98af1ad020a003db66d7319f33d43deec315aec5 100644 --- a/net/minecraft/server/MinecraftServer.java @@ -48,7 +147,7 @@ index 33dd16a26edd2974f04d9a868d3e58e8e3060032..eb0589b203bcf72cd24bb37f2c448c23 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 9af7dafe03812d96aa477584d4147a68c240ab21..539206d55e87ec9968664305caaf475c991fe4d5 100644 +index 9af7dafe03812d96aa477584d4147a68c240ab21..e6fd46b8148e050c4807abf6c8a03e4747bc0da2 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -177,7 +177,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -115,7 +214,7 @@ index 9af7dafe03812d96aa477584d4147a68c240ab21..539206d55e87ec9968664305caaf475c } ); this.tickBlockEntities(); -+ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.unpark(); // Leaf - Async target finding ++ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.tick(); // Leaf - Async target finding } // Paper - rewrite chunk system @@ -206,7 +305,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 this.navigation.tick(); this.customServerAiStep((ServerLevel)this.level()); diff --git a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java -index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b124825798cf103a 100644 +index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..9abb8e7b0dea2cb63dad234812d773403d0716f6 100644 --- a/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java +++ b/net/minecraft/world/entity/ai/goal/AvoidEntityGoal.java @@ -67,15 +67,24 @@ public class AvoidEntityGoal extends Goal { @@ -235,7 +334,7 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 if (this.toAvoid == null) { return false; } else { -@@ -91,6 +100,45 @@ public class AvoidEntityGoal extends Goal { +@@ -91,6 +100,36 @@ public class AvoidEntityGoal extends Goal { } } @@ -258,23 +357,14 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 + final var serverLevel = getServerLevel(mob); + final var avoidClass = this.avoidClass; + final var bound = mob.getBoundingBox().inflate(this.maxDist, 3.0, this.maxDist); -+ ctx.wake = () -> { -+ try { -+ if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { -+ return; -+ } -+ ctx.result = serverLevel.getNearestEntity( -+ serverLevel.getEntitiesOfClass(avoidClass, bound, livingEntity -> true), -+ avoidEntityTargeting, -+ mob, -+ x, -+ y, -+ z -+ ); -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestEntity( ++ serverLevel.getEntitiesOfClass(avoidClass, bound, livingEntity -> true), ++ avoidEntityTargeting, ++ mob, ++ x, ++ y, ++ z ++ ); + } + // Leaf end - Async Avoid Entity Finding + @@ -282,10 +372,10 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..776f18287b6b431b6b33d370b1248257 public boolean canContinueToUse() { return !this.pathNav.isDone(); diff --git a/net/minecraft/world/entity/ai/goal/BegGoal.java b/net/minecraft/world/entity/ai/goal/BegGoal.java -index 28ef40e8a645989ea181297069cf2bbe571f3082..4db188a08c1d13f77fb268ba03ac5d7fb9b71ad0 100644 +index 28ef40e8a645989ea181297069cf2bbe571f3082..d011e4735cb8fd65a39a6b7a66386375b12aca78 100644 --- a/net/minecraft/world/entity/ai/goal/BegGoal.java +++ b/net/minecraft/world/entity/ai/goal/BegGoal.java -@@ -27,8 +27,50 @@ public class BegGoal extends Goal { +@@ -27,8 +27,43 @@ public class BegGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.LOOK)); } @@ -306,18 +396,11 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..4db188a08c1d13f77fb268ba03ac5d7f + final ServerLevel serverLevel = getServerLevel(wolf); + final TargetingConditions begTargeting = this.begTargeting; + ctx.wake = () -> { -+ try { -+ if (wolf.level() != serverLevel || wolf.isRemoved() || !wolf.isAlive()) { -+ return; -+ } -+ -+ var player = serverLevel.getNearestPlayer(begTargeting, wolf); -+ if (player != null && playerHoldingInteresting(player)) { -+ ctx.result = player; -+ } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); ++ var player = serverLevel.getNearestPlayer(begTargeting, wolf); ++ if (player != null && playerHoldingInteresting(player)) { ++ return player; + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -336,7 +419,7 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..4db188a08c1d13f77fb268ba03ac5d7f this.player = this.level.getNearestPlayer(this.begTargeting, this.wolf); return this.player != null && this.playerHoldingInteresting(this.player); } -@@ -59,10 +101,10 @@ public class BegGoal extends Goal { +@@ -59,10 +94,10 @@ public class BegGoal extends Goal { this.lookTime--; } @@ -397,10 +480,10 @@ index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d3 + // Leaf end - Async search block } diff --git a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java -index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995a92433e5 100644 +index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52aa20a82d6 100644 --- a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java -@@ -23,8 +23,54 @@ public class FollowBoatGoal extends Goal { +@@ -23,8 +23,47 @@ public class FollowBoatGoal extends Goal { this.mob = mob; } @@ -418,23 +501,16 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 + final var bound = mob.getBoundingBox().inflate(5.0); + final var serverLevel = getServerLevel(mob); + ctx.wake = () -> { -+ try { -+ if (mob.level() != serverLevel || mob.isRemoved() || !mob.isAlive()) { -+ return; -+ } ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(AbstractBoat.class, bound); ++ for (AbstractBoat abstractBoat : entitiesOfClass) { ++ Entity controllingPassenger = abstractBoat.getControllingPassenger(); ++ if (controllingPassenger instanceof Player player ++ && (Mth.abs(player.xxa) > 0.0F || Mth.abs(player.zza) > 0.0F)) { ++ return player; + -+ List entitiesOfClass = serverLevel.getEntitiesOfClass(AbstractBoat.class, bound); -+ for (AbstractBoat abstractBoat : entitiesOfClass) { -+ Entity controllingPassenger = abstractBoat.getControllingPassenger(); -+ if (controllingPassenger instanceof Player player -+ && (Mth.abs(player.xxa) > 0.0F || Mth.abs(player.zza) > 0.0F)) { -+ ctx.result = player; -+ break; -+ } + } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -455,7 +531,7 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 List entitiesOfClass = this.mob.level().getEntitiesOfClass(AbstractBoat.class, this.mob.getBoundingBox().inflate(5.0)); boolean flag = false; -@@ -37,7 +83,7 @@ public class FollowBoatGoal extends Goal { +@@ -37,7 +76,7 @@ public class FollowBoatGoal extends Goal { } } @@ -465,7 +541,7 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..2d2ffa9e89f4954e096c48cba96db995 @Override diff --git a/net/minecraft/world/entity/ai/goal/FollowMobGoal.java b/net/minecraft/world/entity/ai/goal/FollowMobGoal.java -index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d4133632ae4487 100644 +index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..c2baf746a0697559dc391b6f8c9303917e194836 100644 --- a/net/minecraft/world/entity/ai/goal/FollowMobGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowMobGoal.java @@ -38,6 +38,15 @@ public class FollowMobGoal extends Goal { @@ -484,7 +560,7 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 List entitiesOfClass = this.mob.level().getEntitiesOfClass(Mob.class, this.mob.getBoundingBox().inflate(this.areaSize), this.followPredicate); if (!entitiesOfClass.isEmpty()) { for (Mob mob : entitiesOfClass) { -@@ -51,6 +60,43 @@ public class FollowMobGoal extends Goal { +@@ -51,6 +60,36 @@ public class FollowMobGoal extends Goal { return false; } @@ -505,22 +581,15 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 + final var bound = this.mob.getBoundingBox().inflate(this.areaSize); + final var followPredicate = this.followPredicate; + ctx.wake = () -> { -+ try { -+ if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { -+ return; -+ } -+ List entitiesOfClass = serverLevel.getEntitiesOfClass(Mob.class, bound, followPredicate); -+ if (!entitiesOfClass.isEmpty()) { -+ for (final Mob follow : entitiesOfClass) { -+ if (!follow.isInvisible()) { -+ ctx.result = follow; -+ return; -+ } ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(Mob.class, bound, followPredicate); ++ if (!entitiesOfClass.isEmpty()) { ++ for (final Mob follow : entitiesOfClass) { ++ if (!follow.isInvisible()) { ++ return follow; + } + } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return null; + }; + } + // Leaf end - Async Follow Mob Finding @@ -529,10 +598,10 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..911ceddb4ded974a02355c76e5d41336 public boolean canContinueToUse() { return this.followingMob != null && !this.navigation.isDone() && this.mob.distanceToSqr(this.followingMob) > this.stopDistance * this.stopDistance; diff --git a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java -index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e0100f78e58b 100644 +index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270169330ef 100644 --- a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java -@@ -19,11 +19,66 @@ public class FollowParentGoal extends Goal { +@@ -19,11 +19,56 @@ public class FollowParentGoal extends Goal { this.speedModifier = speedModifier; } @@ -554,30 +623,20 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 + final var serverLevel = getServerLevel(animal); + final var pos = animal.position(); + ctx.wake = () -> { -+ try { -+ if (animal.level() != serverLevel || animal.isRemoved() || !animal.isAlive()) { -+ return; -+ } ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(targetType, bound); ++ Animal target = null; ++ double d = Double.MAX_VALUE; + -+ List entitiesOfClass = serverLevel.getEntitiesOfClass(targetType, bound); -+ Animal target = null; -+ double d = Double.MAX_VALUE; -+ -+ for (Animal animal1 : entitiesOfClass) { -+ if (animal1.getAge() >= 0) { -+ double d1 = animal1.distanceToSqr(pos); -+ if (!(d1 > d)) { -+ d = d1; -+ target = animal1; -+ } ++ for (Animal animal1 : entitiesOfClass) { ++ if (animal1.getAge() >= 0) { ++ double d1 = animal1.distanceToSqr(pos); ++ if (!(d1 > d)) { ++ d = d1; ++ target = animal1; + } + } -+ if (target != null) { -+ ctx.result = target; -+ } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return target; + }; + } + // Leaf end - Async Target Finding @@ -599,7 +658,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 List entitiesOfClass = this.animal .level() .getEntitiesOfClass((Class)this.animal.getClass(), this.animal.getBoundingBox().inflate(8.0, 4.0, 8.0)); -@@ -43,6 +98,7 @@ public class FollowParentGoal extends Goal { +@@ -43,6 +88,7 @@ public class FollowParentGoal extends Goal { if (animal == null) { return false; } else if (d < 9.0) { @@ -608,7 +667,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..80f9de7b2c03c1477dae0f42b328e010 } else { this.parent = animal; diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e6256da54493 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538d289b7b2 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -26,6 +26,13 @@ public class GoalSelector { @@ -625,7 +684,7 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); } -@@ -85,7 +92,111 @@ public class GoalSelector { +@@ -85,7 +92,103 @@ public class GoalSelector { return true; } @@ -714,14 +773,6 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 + } + return false; + } -+ -+ public final void wake() { -+ Runnable wake = ctx.wake; -+ if (wake != null) { -+ wake.run(); -+ } -+ ctx.wake = null; -+ } + // Leaf end - Async target finding + public void tick() { @@ -737,11 +788,27 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..ec56b4e8acd2dad23a94b1fe9145e625 for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); +@@ -116,6 +219,15 @@ public class GoalSelector { + } + + public void tickRunningGoals(boolean tickAllRunning) { ++ // Leaf start - Async target finding ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ if (this.ctxGoals == null) { ++ this.ctxState = 2; ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ } ++ return; ++ } ++ // Leaf end - Async target finding + for (WrappedGoal wrappedGoal : this.availableGoals) { + if (wrappedGoal.isRunning() && (tickAllRunning || wrappedGoal.requiresUpdateEveryTick())) { + wrappedGoal.tick(); diff --git a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd3f0bfae2 100644 +index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0bff40c42 100644 --- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java +++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java -@@ -20,20 +20,93 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -20,20 +20,83 @@ public class LlamaFollowCaravanGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } @@ -761,21 +828,28 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd + final var serverLevel = getServerLevel(llama); + final var pos = llama.position(); + ctx.wake = () -> { -+ try { -+ if (llama.level() != serverLevel || llama.isRemoved() || !llama.isAlive()) { -+ return; ++ List entities = serverLevel.getEntities(llama, bound, entity1 -> { ++ EntityType type = entity1.getType(); ++ return type == EntityType.LLAMA || type == EntityType.TRADER_LLAMA; ++ }); ++ Llama target = null; ++ double d = Double.MAX_VALUE; ++ ++ for (Entity entity : entities) { ++ Llama llama1 = (Llama) entity; ++ if (llama1.inCaravan() && !llama1.hasCaravanTail()) { ++ double d1 = llama1.distanceToSqr(pos); ++ if (!(d1 > d)) { ++ d = d1; ++ target = llama1; ++ } + } ++ } + -+ List entities = serverLevel.getEntities(llama, bound, entity1 -> { -+ EntityType type = entity1.getType(); -+ return type == EntityType.LLAMA || type == EntityType.TRADER_LLAMA; -+ }); -+ Llama target = null; -+ double d = Double.MAX_VALUE; -+ -+ for (Entity entity : entities) { -+ Llama llama1 = (Llama) entity; -+ if (llama1.inCaravan() && !llama1.hasCaravanTail()) { ++ if (target == null) { ++ for (Entity entityx : entities) { ++ Llama llama1 = (Llama) entityx; ++ if (llama1.isLeashed() && !llama1.hasCaravanTail()) { + double d1 = llama1.distanceToSqr(pos); + if (!(d1 > d)) { + d = d1; @@ -783,25 +857,8 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd + } + } + } -+ -+ if (target == null) { -+ for (Entity entityx : entities) { -+ Llama llama1 = (Llama) entityx; -+ if (llama1.isLeashed() && !llama1.hasCaravanTail()) { -+ double d1 = llama1.distanceToSqr(pos); -+ if (!(d1 > d)) { -+ d = d1; -+ target = llama1; -+ } -+ } -+ } -+ } -+ if (target != null) { -+ ctx.result = target; -+ } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return target; + }; + } + // Leaf end - Async Target Finding @@ -838,7 +895,7 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; -@@ -45,7 +118,7 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -45,7 +108,7 @@ public class LlamaFollowCaravanGoal extends Goal { if (llama == null) { for (Entity entityx : entities) { Llama llama1 = (Llama)entityx; @@ -847,7 +904,7 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; -@@ -54,6 +127,7 @@ public class LlamaFollowCaravanGoal extends Goal { +@@ -54,6 +117,7 @@ public class LlamaFollowCaravanGoal extends Goal { } } } @@ -856,10 +913,10 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..7c54964a5329e12a9a4a1fdb2417ccbd if (llama == null) { return false; diff --git a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java -index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0ee28b9446 100644 +index 6463c3c9b08d6058f2843c225b08a40fc30a960b..98c2b4a298ada4b02afa55f991791d8696702181 100644 --- a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java +++ b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java -@@ -48,32 +48,87 @@ public class LookAtPlayerGoal extends Goal { +@@ -48,32 +48,79 @@ public class LookAtPlayerGoal extends Goal { @Override public boolean canUse() { @@ -899,28 +956,15 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e - ServerLevel serverLevel = getServerLevel(this.mob); - if (this.lookAtType == Player.class) { - this.lookAt = serverLevel.getNearestPlayer(this.lookAtContext, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); -- } else { -- this.lookAt = serverLevel.getNearestEntity( -- this.mob -- .level() -- .getEntitiesOfClass(this.lookAtType, this.mob.getBoundingBox().inflate(this.lookDistance, 3.0, this.lookDistance), livingEntity -> true), -- this.lookAtContext, -- this.mob, -- this.mob.getX(), -- this.mob.getEyeY(), -- this.mob.getZ() -- ); -- } + return this.lookAt != null; + } - -- return this.lookAt != null; ++ + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + if (this.mob.getTarget() != null) { + this.lookAt = this.mob.getTarget(); + return true; - } ++ } + ServerLevel serverLevel = getServerLevel(this.mob); + if (!target.isAlive() || !this.lookAtContext.test(serverLevel, this.mob, target)) return false; + this.lookAt = target; @@ -939,27 +983,31 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e + final var lookAtContext = this.lookAtContext; + final var lookAtType = this.lookAtType; + ctx.wake = () -> { -+ try { -+ if (mob == null || !mob.isAlive() || mob.level() != serverLevel) { -+ return; -+ } -+ -+ if (lookAtType == Player.class) { -+ ctx.result = serverLevel.getNearestPlayer(lookAtContext, mob, x, y, z); -+ } else { -+ ctx.result = serverLevel.getNearestEntity( -+ serverLevel -+ .getEntitiesOfClass(lookAtType, bound, livingEntity -> true), -+ lookAtContext, -+ mob, -+ x, -+ y, -+ z -+ ); -+ } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); -+ } ++ if (lookAtType == Player.class) { ++ return serverLevel.getNearestPlayer(lookAtContext, mob, x, y, z); + } else { +- this.lookAt = serverLevel.getNearestEntity( +- this.mob +- .level() +- .getEntitiesOfClass(this.lookAtType, this.mob.getBoundingBox().inflate(this.lookDistance, 3.0, this.lookDistance), livingEntity -> true), +- this.lookAtContext, +- this.mob, +- this.mob.getX(), +- this.mob.getEyeY(), +- this.mob.getZ() ++ return serverLevel.getNearestEntity( ++ serverLevel ++ .getEntitiesOfClass(lookAtType, bound, livingEntity -> true), ++ lookAtContext, ++ mob, ++ x, ++ y, ++ z + ); + } +- +- return this.lookAt != null; +- } + }; } + // Leaf end - Async look finding @@ -967,10 +1015,10 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..0afad8ddb0e9d083d47b96627d999f0e @Override public boolean canContinueToUse() { diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539fd0603b4 100644 +index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e7c93afca 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -@@ -41,8 +41,69 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -41,8 +41,60 @@ public abstract class MoveToBlockGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.JUMP)); } @@ -1005,16 +1053,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + final BlockPos blockPos = mob.blockPosition(); + final float restrictRadius = mob.getRestrictRadius(); + final BlockPos restrictCenter = mob.getRestrictCenter(); -+ ctx.wake = () -> { -+ try { -+ if (mob == null || !mob.isAlive() || mob.level() != serverLevel) { -+ return; -+ } -+ findNearestBlockAsync(ctx, ty, toRemove, mob, serverLevel, verticalSearchStart, searchRange, verticalSearchRange, blockPos, restrictRadius, restrictCenter); -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting block", e); -+ } -+ }; ++ ctx.wake = () -> findNearestBlockAsync(ty, toRemove, mob, serverLevel, verticalSearchStart, searchRange, verticalSearchRange, blockPos, restrictRadius, restrictCenter); + } + + protected enum TypeToCheck { @@ -1040,7 +1079,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 if (this.nextStartTick > 0) { this.nextStartTick--; return false; -@@ -109,6 +170,12 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -109,6 +161,12 @@ public abstract class MoveToBlockGoal extends Goal { } protected boolean findNearestBlock() { @@ -1053,13 +1092,12 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 int i = this.searchRange; int i1 = this.verticalSearchRange; BlockPos blockPos = this.mob.blockPosition(); -@@ -133,5 +200,108 @@ public abstract class MoveToBlockGoal extends Goal { +@@ -133,5 +191,105 @@ public abstract class MoveToBlockGoal extends Goal { return false; } + // Leaf start - Async search block -+ protected static boolean findNearestBlockAsync( -+ final org.dreeam.leaf.async.ai.Waker ctx, ++ protected static @javax.annotation.Nullable BlockPos findNearestBlockAsync( + final TypeToCheck ty, + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.block.Block toRemove, + final PathfinderMob mob, @@ -1080,15 +1118,13 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + if (!serverLevel.hasChunkAt(mutableBlockPos)) continue; + if (isWithinRestriction(restrictRadius, restrictCenter, mutableBlockPos) + && isValidTargetAsync(ty, toRemove, mob, serverLevel, mutableBlockPos)) { -+ ctx.result = mutableBlockPos.immutable(); -+ return true; ++ return mutableBlockPos.immutable(); + } + } + } + } + } -+ -+ return false; ++ return null; + } + + private static boolean isWithinRestriction(float restrictRadius, BlockPos restrictCenter, BlockPos pos) { @@ -1163,7 +1199,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..292749c786b85f86fa5f65c49fa83539 + } } diff --git a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java -index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3540066f4 100644 +index 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80691ac1bc 100644 --- a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java +++ b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java @@ -19,10 +19,20 @@ public class OfferFlowerGoal extends Goal { @@ -1179,16 +1215,16 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3 + return true; + } + if (this.golem.getRandom().nextInt(8000) != 0) { -+ return false; + return false; + } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getVillagerAsync(); - return false; ++ return false; + // Leaf end - Async offer flower finding } else { this.villager = getServerLevel(this.golem) .getNearestEntity( -@@ -38,6 +48,45 @@ public class OfferFlowerGoal extends Goal { +@@ -38,6 +48,36 @@ public class OfferFlowerGoal extends Goal { } } @@ -1211,23 +1247,14 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..e1d185b2f5e7eb7a00501ff85b4e16d3 + final double z = golem.getZ(); + final var serverLevel = getServerLevel(golem); + final var bound = golem.getBoundingBox().inflate(6.0, 2.0, 6.0); -+ ctx.wake = () -> { -+ try { -+ if (golem == null || !golem.isAlive() || golem.level() != serverLevel) { -+ return; -+ } -+ ctx.result = serverLevel.getNearestEntity( -+ serverLevel.getEntitiesOfClass(Villager.class, bound, livingEntity -> true), -+ OFFER_TARGER_CONTEXT, -+ golem, -+ x, -+ y, -+ z -+ ); -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestEntity( ++ serverLevel.getEntitiesOfClass(Villager.class, bound, livingEntity -> true), ++ OFFER_TARGER_CONTEXT, ++ golem, ++ x, ++ y, ++ z ++ ); + } + // Leaf end - Async look finding + @@ -1272,10 +1299,10 @@ index c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700 + // Leaf end - Async search block } diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java -index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f9218561aa8 100644 +index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50e9e62b34 100644 --- a/net/minecraft/world/entity/ai/goal/TemptGoal.java +++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java -@@ -36,12 +36,60 @@ public class TemptGoal extends Goal { +@@ -36,12 +36,51 @@ public class TemptGoal extends Goal { this.targetingConditions = TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity)); } @@ -1296,16 +1323,7 @@ index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f92 + final var conditions = this.targetingConditions + .range(mob.getAttributeValue(Attributes.TEMPT_RANGE)) + .copy(); -+ ctx.wake = () -> { -+ try { -+ if (mob == null || mob.isRemoved() || !mob.isAlive() || mob.level() != serverLevel) { -+ return; -+ } -+ ctx.result = serverLevel.getNearestPlayer(conditions, mob); -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting player", e); -+ } -+ }; ++ ctx.wake = () -> serverLevel.getNearestPlayer(conditions, mob); + } + // Leaf end - Async Tempt Finding @Override @@ -1337,7 +1355,7 @@ index f88f618d34fb343b31de3af1a875d6633703df71..e42f77fc6d04a900fd8adb4682678f92 .getNearestPlayer(this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)), this.mob); // CraftBukkit start diff --git a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java -index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b95c586fc 100644 +index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7abf8559358 100644 --- a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java @@ -16,7 +16,7 @@ public class DefendVillageTargetGoal extends TargetGoal { @@ -1349,7 +1367,7 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b public DefendVillageTargetGoal(IronGolem golem) { super(golem, false, true); -@@ -24,8 +24,58 @@ public class DefendVillageTargetGoal extends TargetGoal { +@@ -24,8 +24,49 @@ public class DefendVillageTargetGoal extends TargetGoal { this.setFlags(EnumSet.of(Goal.Flag.TARGET)); } @@ -1370,26 +1388,17 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b + AABB bound = this.golem.getBoundingBox().inflate(10.0, 8.0, 10.0); + final ServerLevel serverLevel = getServerLevel(mob); + ctx.wake = () -> { -+ try { -+ if (mob.level() != serverLevel || !mob.isAlive()) { -+ return; -+ } -+ -+ List nearbyEntities = serverLevel.getNearbyEntities(Villager.class, attackTargeting, mob, bound); -+ List nearbyPlayers = serverLevel.getNearbyPlayers(attackTargeting, mob, bound); -+ -+ for (Villager villager : nearbyEntities) { -+ for (Player player : nearbyPlayers) { -+ int playerReputation = villager.getPlayerReputation(player); -+ if (playerReputation <= -100) { -+ ctx.result = player; -+ break; -+ } ++ List nearbyEntities = serverLevel.getNearbyEntities(Villager.class, attackTargeting, mob, bound); ++ List nearbyPlayers = serverLevel.getNearbyPlayers(attackTargeting, mob, bound); ++ for (Villager villager : nearbyEntities) { ++ for (Player player : nearbyPlayers) { ++ int playerReputation = villager.getPlayerReputation(player); ++ if (playerReputation <= -100) { ++ return player; + } + } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); + } ++ return null; + }; + } + // Leaf end - Async Target Finding @@ -1408,7 +1417,7 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b AABB aabb = this.golem.getBoundingBox().inflate(10.0, 8.0, 10.0); ServerLevel serverLevel = getServerLevel(this.golem); List nearbyEntities = serverLevel.getNearbyEntities(Villager.class, this.attackTargeting, this.golem, aabb); -@@ -43,7 +93,7 @@ public class DefendVillageTargetGoal extends TargetGoal { +@@ -43,7 +84,7 @@ public class DefendVillageTargetGoal extends TargetGoal { } return this.potentialTarget != null @@ -1418,10 +1427,10 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..941131fd448216afe2bf46dc6aab1d4b @Override diff --git a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc590812168dd24cd6 100644 +index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d7711702e97 100644 --- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -@@ -73,6 +73,56 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -73,6 +73,49 @@ public class HurtByTargetGoal extends TargetGoal { protected void alertOthers() { double followDistance = this.getFollowDistance(); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followDistance, 10.0, followDistance); @@ -1434,42 +1443,35 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 + final var serverLevel = getServerLevel(self); + final var toIgnoreAlert = this.toIgnoreAlert; + ctx.wake = () -> { -+ try { -+ if (self == null || self.level() != serverLevel) { -+ return; -+ } -+ var toAlert = new java.util.ArrayList(); -+ List entitiesOfClass = serverLevel -+ .getEntitiesOfClass(self.getClass(), aabb, EntitySelector.NO_SPECTATORS); -+ for (Mob mob : entitiesOfClass) { -+ if (self != mob -+ && mob.getTarget() == null -+ && (!(self instanceof TamableAnimal) || ((TamableAnimal) self).getOwner() == ((TamableAnimal) mob).getOwner()) -+ && !mob.isAlliedTo(self.getLastHurtByMob())) { -+ if (toIgnoreAlert == null) { -+ continue; -+ } -+ -+ boolean flag = false; -+ -+ for (Class clazz : toIgnoreAlert) { -+ if (mob.getClass() == clazz) { -+ flag = true; -+ break; -+ } -+ } -+ -+ if (!flag) { -+ continue; -+ } -+ -+ toAlert.add(mob); ++ var toAlert = new java.util.ArrayList(); ++ List entitiesOfClass = serverLevel ++ .getEntitiesOfClass(self.getClass(), aabb, EntitySelector.NO_SPECTATORS); ++ for (Mob mob : entitiesOfClass) { ++ if (self != mob ++ && mob.getTarget() == null ++ && (!(self instanceof TamableAnimal) || ((TamableAnimal) self).getOwner() == ((TamableAnimal) mob).getOwner()) ++ && !mob.isAlliedTo(self.getLastHurtByMob())) { ++ if (toIgnoreAlert == null) { ++ continue; + } ++ ++ boolean flag = false; ++ ++ for (Class clazz : toIgnoreAlert) { ++ if (mob.getClass() == clazz) { ++ flag = true; ++ break; ++ } ++ } ++ ++ if (!flag) { ++ continue; ++ } ++ ++ toAlert.add(mob); + } -+ ctx.result = toAlert; -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception alertOthers", e); + } ++ return toAlert; + }; + return; + } @@ -1478,7 +1480,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 List entitiesOfClass = this.mob .level() .getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); -@@ -87,7 +137,7 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -87,7 +130,7 @@ public class HurtByTargetGoal extends TargetGoal { mob = (Mob)var5.next(); if (this.mob != mob @@ -1487,7 +1489,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 && (!(this.mob instanceof TamableAnimal) || ((TamableAnimal)this.mob).getOwner() == ((TamableAnimal)mob).getOwner()) && !mob.isAlliedTo(this.mob.getLastHurtByMob())) { if (this.toIgnoreAlert == null) { -@@ -96,7 +146,7 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -96,7 +139,7 @@ public class HurtByTargetGoal extends TargetGoal { boolean flag = false; @@ -1496,15 +1498,30 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 if (mob.getClass() == clazz) { flag = true; break; -@@ -113,6 +163,15 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -113,6 +156,30 @@ public class HurtByTargetGoal extends TargetGoal { } } + // Leaf start - Async alert other + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toAlert)) return; -+ for (var livingEntity : toAlert) { -+ alertOther((Mob) livingEntity, this.mob.getLastHurtByMob()); ++ LivingEntity lastHurtByMob = this.mob.getLastHurtByMob(); ++ if (lastHurtByMob.getType() == EntityType.PLAYER && getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) { ++ return; ++ } ++ for (Class clazz : this.toIgnoreDamage) { ++ if (clazz.isAssignableFrom(lastHurtByMob.getClass())) { ++ return; ++ } ++ } ++ if (!this.canAttack(lastHurtByMob, HURT_BY_TARGETING)) { ++ return; ++ } ++ for (var obj : toAlert) { ++ Mob mob = (Mob) obj; ++ if (EntitySelector.NO_SPECTATORS.test(mob) && mob.getTarget() == null && mob.isAlliedTo(this.mob.getLastHurtByMob())) { ++ alertOther(mob, this.mob.getLastHurtByMob()); ++ } + } + } + // Leaf end - Async alert other @@ -1513,10 +1530,10 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..6998e7a102b711152d78f4dc59081216 mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason } diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java -index 85eae0a14f7a417dfd8c911079d05354a98e5834..3d0c28caa646286be79cefee4710b15f9aad928c 100644 +index 85eae0a14f7a417dfd8c911079d05354a98e5834..f59d5c9be0eb10f5b5192442e1850900d71a31e9 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java -@@ -41,8 +41,51 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -41,8 +41,43 @@ public class NearestAttackableTargetGoal extends TargetG this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(selector); } @@ -1541,18 +1558,10 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..3d0c28caa646286be79cefee4710b15f + final AABB targetSearch = getTargetSearchArea(this.getFollowDistance()); + final ServerLevel serverLevel = getServerLevel(mob); + ctx.wake = () -> { -+ try { -+ if (mob.level() != serverLevel || mob.isRemoved() || !mob.isAlive()) { -+ return; -+ } -+ -+ if (targetType != Player.class && targetType != ServerPlayer.class) { -+ ctx.result = serverLevel.getNearestEntity(mob.level().getEntitiesOfClass(targetType, targetSearch, entity -> entity != null && entity != mob && entity.isAlive()), targetConditions, mob, x,y,z); -+ } else { -+ ctx.result = serverLevel.getNearestPlayer(targetConditions, mob, x, y, z); -+ } -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception getting entities", e); ++ if (targetType != Player.class && targetType != ServerPlayer.class) { ++ return serverLevel.getNearestEntity(mob.level().getEntitiesOfClass(targetType, targetSearch, entity -> entity != null && entity != mob && entity.isAlive()), targetConditions, mob, x, y, z); ++ } else { ++ return serverLevel.getNearestPlayer(targetConditions, mob, x, y, z); + } + }; + } @@ -1568,7 +1577,7 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..3d0c28caa646286be79cefee4710b15f if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) { return false; } else { -@@ -57,6 +100,15 @@ public class NearestAttackableTargetGoal extends TargetG +@@ -57,6 +92,15 @@ public class NearestAttackableTargetGoal extends TargetG protected void findTarget() { ServerLevel serverLevel = getServerLevel(this.mob); @@ -1627,10 +1636,10 @@ index abf57494950f55bbd75f335f26736cb9e703c197..efd2418f56c36e7850edde483a2a4906 } } diff --git a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java -index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb76144b37 100644 +index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..270f0b8b33aed1c54edbdb8595ce7fcc7fca2dc4 100644 --- a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java -@@ -37,6 +37,34 @@ public class ResetUniversalAngerTargetGoal extends G +@@ -37,6 +37,27 @@ public class ResetUniversalAngerTargetGoal extends G this.lastHurtByPlayerTimestamp = this.mob.getLastHurtByMobTimestamp(); this.mob.forgetCurrentTargetAndRefreshUniversalAnger(); if (this.alertOthersOfSameType) { @@ -1643,21 +1652,14 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb + final double followRange = this.mob.getAttributeValue(Attributes.FOLLOW_RANGE); + final AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followRange, 10.0, followRange); + ctx.wake = () -> { -+ try { -+ if (mob == null || serverLevel != mob.level() || !mob.isAlive()) { -+ return; ++ var entities = serverLevel.getEntitiesOfClass(mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); ++ List toStop = new java.util.ArrayList<>(entities.size()); ++ for (Mob entity : entities) { ++ if (entity != mob) { ++ toStop.add((NeutralMob) entity); + } -+ var entities = serverLevel.getEntitiesOfClass(mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); -+ List toStop = new java.util.ArrayList<>(entities.size()); -+ for (Mob entity : entities) { -+ if (entity != mob) { -+ toStop.add((NeutralMob) entity); -+ } -+ } -+ ctx.result = toStop; -+ } catch (Exception e) { -+ org.dreeam.leaf.async.ai.AsyncGoalExecutor.LOGGER.error("Exception alertOthers forgetCurrentTargetAndRefreshUniversalAnger", e); + } ++ return toStop; + }; + return; + } @@ -1665,7 +1667,7 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb this.getNearbyMobsOfSameType() .stream() .filter(mob -> mob != this.mob) -@@ -47,7 +75,17 @@ public class ResetUniversalAngerTargetGoal extends G +@@ -47,7 +68,19 @@ public class ResetUniversalAngerTargetGoal extends G super.start(); } @@ -1673,7 +1675,9 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..15da909b4db3d7a7532aed550208ecbb + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toStop)) return; + for (var neutralMob : toStop) { -+ ((NeutralMob)neutralMob).forgetCurrentTargetAndRefreshUniversalAnger(); ++ if (EntitySelector.NO_SPECTATORS.test((net.minecraft.world.entity.Entity) neutralMob)) { ++ ((NeutralMob) neutralMob).forgetCurrentTargetAndRefreshUniversalAnger(); ++ } + } + } + // Leaf end - Async alert other @@ -1913,6 +1917,45 @@ index d44ed0d6a672a0b1eb0a8781e3e094096a2b753d..490c196419c94b75a540e64d513163df } static class DrownedGoToWaterGoal extends Goal { +diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java +index c7897532163d4fdf5a82982f7d24a47dd61e3dfa..2986384389c1acd311f4367d622550830ce009c1 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -597,10 +597,34 @@ public class EnderMan extends Monster implements NeutralMob { + + @Override + public boolean canUse() { ++ // Leaf start - Async Target Finding ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { ++ if (poll()) { ++ return true; ++ } ++ final EnderMan enderman = this.enderman; ++ final var ctx = enderman.getGoalCtx(); ++ if (!ctx.state) return false; ++ final var level = getServerLevel(this.enderman); ++ final var cond = this.startAggroTargetConditions.range(this.getFollowDistance()).copy(); ++ ctx.wake = () -> level.getNearestPlayer(cond, enderman); ++ return false; ++ } ++ // Leaf end - Async Target Finding + this.pendingTarget = getServerLevel(this.enderman).getNearestPlayer(this.startAggroTargetConditions.range(this.getFollowDistance()), this.enderman); + return this.pendingTarget != null; + } + ++ // Leaf start - Async Target Finding ++ protected boolean poll() { ++ if (!(this.mob.getGoalCtx().result() instanceof Player player)) return false; ++ var serverLevel = getServerLevel(this.enderman); ++ if (!this.startAggroTargetConditions.range(this.getFollowDistance()).test(serverLevel, enderman, player)) return false; ++ this.pendingTarget = player; ++ return true; ++ } ++ // Leaf end - Async Target Finding ++ + @Override + public void start() { + this.aggroTime = this.adjustedTickDelay(5); diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java index ae4ee948971e931e4fdc4ec2187f5182195c626c..f4fa19c6352e44a624e81dc201b1d7d710c2d9d2 100644 --- a/net/minecraft/world/entity/monster/Strider.java diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch index 16a14fa4..3f1d986d 100644 --- a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch +++ b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7df3222828 100644 +index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d9fc6fdd8 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -@@ -382,6 +382,13 @@ public final class ChunkEntitySlices { +@@ -414,6 +414,13 @@ public final class ChunkEntitySlices { private E[] storage; private int size; @@ -22,7 +22,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d public BasicEntityList() { this(0); -@@ -402,6 +409,7 @@ public final class ChunkEntitySlices { +@@ -434,6 +441,7 @@ public final class ChunkEntitySlices { private void resize() { if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations this.storage = (E[])new Entity[DEFAULT_CAPACITY]; @@ -30,7 +30,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d } else { this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); } -@@ -415,6 +423,7 @@ public final class ChunkEntitySlices { +@@ -447,6 +455,7 @@ public final class ChunkEntitySlices { } else { this.storage[idx] = entity; } @@ -38,7 +38,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7d } public int indexOf(final E entity) { -@@ -430,24 +439,32 @@ public final class ChunkEntitySlices { +@@ -462,24 +471,32 @@ public final class ChunkEntitySlices { } public boolean remove(final E entity) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index b02ad4ab..0f154e37 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -13,19 +13,19 @@ import java.util.concurrent.locks.LockSupport; public class AsyncGoalExecutor { - public static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); - + protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); protected final SpscIntQueue queue; protected final SpscIntQueue wake; + protected final SpscIntQueue submit; private final AsyncGoalThread thread; private final ServerLevel world; - private boolean dirty = false; - private long tickCount = 0L; + private long midTickCount = 0L; public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) { this.world = world; this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize); this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize); + this.submit = new SpscIntQueue(AsyncTargetFinding.queueSize); this.thread = thread; } @@ -34,24 +34,33 @@ public class AsyncGoalExecutor { if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } - mob.goalSelector.wake(); - mob.targetSelector.wake(); + mob.goalSelector.ctx.wake(); + mob.targetSelector.ctx.wake(); return true; } public final void submit(int entityId) { - dirty = true; - if (!this.queue.send(entityId)) { - unpark(); - do { + if (!this.submit.send(entityId)) { + while (poll(entityId)) { wake(entityId); - } while (poll(entityId)); + } } } - public final void unpark() { - if (dirty) LockSupport.unpark(thread); - dirty = false; + public final void tick() { + while (true) { + OptionalInt result = this.submit.recv(); + if (result.isEmpty()) { + break; + } + int id = result.getAsInt(); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); + } + } + LockSupport.unpark(thread); } public final void midTick() { @@ -61,26 +70,51 @@ public class AsyncGoalExecutor { break; } int id = result.getAsInt(); - if (poll(id)) { - submit(id); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); } } - if ((tickCount % AsyncTargetFinding.threshold) == 0L) { - unpark(); + if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) { + boolean submitted = false; + while (true) { + OptionalInt result = this.submit.recv(); + if (result.isEmpty()) { + break; + } + submitted = true; + int id = result.getAsInt(); + if (poll(id) && !this.queue.send(id)) { + do { + wake(id); + } while (poll(id)); + } + } + if (submitted) { + LockSupport.unpark(thread); + } } - tickCount += 1; + + midTickCount += 1; } private boolean poll(int id) { Entity entity = this.world.getEntities().get(id); - if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) { + if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } - mob.tickingTarget = true; - boolean a = mob.targetSelector.poll(); - mob.tickingTarget = false; - boolean b = mob.goalSelector.poll(); - return a || b; + try { + mob.tickingTarget = true; + boolean a = mob.targetSelector.poll(); + mob.tickingTarget = false; + boolean b = mob.goalSelector.poll(); + return a || b; + } catch (Exception e) { + LOGGER.error("Exception while polling", e); + // retry + return true; + } } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java new file mode 100644 index 00000000..f664cfbf --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/VWaker.java @@ -0,0 +1,8 @@ +package org.dreeam.leaf.async.ai; + +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface VWaker { + @Nullable Object wake(); +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java index 0f064e5e..37e557ef 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java @@ -5,14 +5,26 @@ import org.jetbrains.annotations.Nullable; public class Waker { @Nullable - public volatile Runnable wake = null; + public volatile VWaker wake = null; @Nullable public volatile Object result = null; - public volatile boolean state = true; + public boolean state = true; public final @Nullable Object result() { Object result = this.result; this.result = null; return result; } + + final void wake() { + final var wake = this.wake; + if (wake != null) { + try { + this.result = wake.wake(); + } catch (Exception e) { + AsyncGoalExecutor.LOGGER.error("Exception while wake", e); + } + this.wake = null; + } + } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index 479eb133..a444f218 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -34,8 +34,6 @@ public class AsyncTargetFinding extends ConfigModules { asyncTargetFindingInitialized = true; enabled = config.getBoolean(getBasePath() + ".enabled", enabled); - // TODO - 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); @@ -45,7 +43,7 @@ public class AsyncTargetFinding extends ConfigModules { if (queueSize <= 0) { queueSize = 4096; } - if (threshold <= 0L) { + if (threshold == 0L) { threshold = 10L; } if (!enabled) { From 7691e546c26a90ea0c05a83b11a6f485bc2f9e13 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 13 May 2025 22:22:21 +0200 Subject: [PATCH 53/81] HashedReferenceList on WeightedRandomList --- ...Use-HashedList-on-WeightedRandomList.patch | 43 +++ .../leaf/util/list/HashedReferenceList.java | 302 ++++++++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java diff --git a/leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch b/leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch new file mode 100644 index 00000000..4508b07c --- /dev/null +++ b/leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Tue, 13 May 2025 20:41:07 +0200 +Subject: [PATCH] Use HashedList on WeightedRandomList + + +diff --git a/net/minecraft/util/random/WeightedRandomList.java b/net/minecraft/util/random/WeightedRandomList.java +index d81648b9159b765ea3ff99abd46c065ac7f08d6b..76678beea53dac2fa7c394e97a6616a32ac08a64 100644 +--- a/net/minecraft/util/random/WeightedRandomList.java ++++ b/net/minecraft/util/random/WeightedRandomList.java +@@ -2,6 +2,8 @@ package net.minecraft.util.random; + + import com.google.common.collect.ImmutableList; + import com.mojang.serialization.Codec; ++ ++import java.util.Collections; + import java.util.List; + import java.util.Objects; + import java.util.Optional; +@@ -11,10 +13,12 @@ import net.minecraft.util.RandomSource; + public class WeightedRandomList { + private final int totalWeight; + private final ImmutableList items; ++ private List entryHashList; + + WeightedRandomList(List items) { + this.items = ImmutableList.copyOf(items); + this.totalWeight = WeightedRandom.getTotalWeight(items); ++ this.entryHashList = this.items.size() > 4 ? this.items : Collections.unmodifiableList(new org.dreeam.leaf.util.list.HashedReferenceList(this.items)); + } + + public static WeightedRandomList create() { +@@ -43,9 +47,7 @@ public class WeightedRandomList { + } + } + +- public List unwrap() { +- return this.items; +- } ++ public List unwrap() {return this.entryHashList;} + + public static Codec> codec(Codec elementCodec) { + return elementCodec.listOf().xmap(WeightedRandomList::create, WeightedRandomList::unwrap); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java new file mode 100644 index 00000000..6a66cf99 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java @@ -0,0 +1,302 @@ +package org.dreeam.leaf.util.list; + +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import org.jetbrains.annotations.NotNull; + +/** + * A List implementation that maintains a hash-based counter for O(1) element lookup. + * Combines an array-based list for order with a hash map for fast containment checks. + */ +public class HashedReferenceList + implements List { + // The actual ordered storage of elements + private final ReferenceArrayList list = new ReferenceArrayList<>(); + // Tracks occurrence count of each element for O(1) contains checks + private final Reference2IntOpenHashMap counter; + + /** + * Creates a new HashedReferenceList containing all elements from the provided list + * while building a counter map for fast lookups. + */ + public HashedReferenceList(List list) { + this.list.addAll(list); + this.counter = new Reference2IntOpenHashMap<>(); + this.counter.defaultReturnValue(0); + for (T obj : this.list) { + this.counter.addTo(obj, 1); + } + } + + @Override + public int size() { + return this.list.size(); + } + + @Override + public boolean isEmpty() { + return this.list.isEmpty(); + } + + /** + * Checks if an element exists in the list in O(1) time using the counter map. + */ + @Override + public boolean contains(Object o) { + return this.counter.containsKey(o); + } + + @Override + public Iterator iterator() { + return this.listIterator(); + } + + @Override + public Object[] toArray() { + return this.list.toArray(); + } + + @Override + public T1[] toArray(T1 @NotNull [] a) { + return this.list.toArray(a); + } + + /** + * Adds an element and updates the counter map. + */ + @Override + public boolean add(T t) { + this.trackReferenceAdded(t); + return this.list.add(t); + } + + /** + * Removes an element and updates the counter map. + */ + @Override + public boolean remove(Object o) { + this.trackReferenceRemoved(o); + return this.list.remove(o); + } + + /** + * Checks if all elements of the collection exist in this list. + */ + @Override + public boolean containsAll(Collection c) { + for (Object obj : c) { + if (this.counter.containsKey(obj)) continue; + return false; + } + return true; + } + + @Override + public boolean addAll(Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + return this.list.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + return this.list.addAll(index, c); + } + + /** + * Optimizes removal by converting to a hash set for large operations. + */ + @Override + public boolean removeAll(@NotNull Collection c) { + if (this.size() >= 2 && c.size() > 4 && c instanceof List) { + c = new ReferenceOpenHashSet<>(c); + } + this.counter.keySet().removeAll(c); + return this.list.removeAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + this.counter.keySet().retainAll(c); + return this.list.retainAll(c); + } + + @Override + public void clear() { + this.counter.clear(); + this.list.clear(); + } + + @Override + public T get(int index) { + return this.list.get(index); + } + + /** + * Sets an element at specific index while maintaining accurate counts. + */ + @Override + public T set(int index, T element) { + T prev = this.list.set(index, element); + if (prev != element) { + if (prev != null) { + this.trackReferenceRemoved(prev); + } + this.trackReferenceAdded(element); + } + return prev; + } + + @Override + public void add(int index, T element) { + this.trackReferenceAdded(element); + this.list.add(index, element); + } + + @Override + public T remove(int index) { + T prev = this.list.remove(index); + if (prev != null) { + this.trackReferenceRemoved(prev); + } + return prev; + } + + @Override + public int indexOf(Object o) { + return this.list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.list.lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + return this.listIterator(0); + } + + /** + * Custom ListIterator implementation that maintains counter consistency. + */ + @Override + public ListIterator listIterator(final int index) { + return new ListIterator<>() { + private final ListIterator inner; + + { + this.inner = HashedReferenceList.this.list.listIterator(index); + } + + @Override + public boolean hasNext() { + return this.inner.hasNext(); + } + + @Override + public T next() { + return this.inner.next(); + } + + @Override + public boolean hasPrevious() { + return this.inner.hasPrevious(); + } + + @Override + public T previous() { + return this.inner.previous(); + } + + @Override + public int nextIndex() { + return this.inner.nextIndex(); + } + + @Override + public int previousIndex() { + return this.inner.previousIndex(); + } + + /** + * Removes the current element and updates counter. + */ + @Override + public void remove() { + int last = this.previousIndex(); + if (last == -1) { + throw new NoSuchElementException(); + } + Object prev = HashedReferenceList.this.get(last); + if (prev != null) { + HashedReferenceList.this.trackReferenceRemoved(prev); + } + this.inner.remove(); + } + + /** + * Sets the current element and updates counter. + */ + @Override + public void set(T t) { + int last = this.previousIndex(); + if (last == -1) { + throw new NoSuchElementException(); + } + Object prev = HashedReferenceList.this.get(last); + if (prev != t) { + if (prev != null) { + HashedReferenceList.this.trackReferenceRemoved(prev); + } + HashedReferenceList.this.trackReferenceAdded(t); + } + this.inner.remove(); + } + + @Override + public void add(T t) { + HashedReferenceList.this.trackReferenceAdded(t); + this.inner.add(t); + } + }; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return this.list.subList(fromIndex, toIndex); + } + + /** + * Increments the reference counter for an added element. + */ + private void trackReferenceAdded(T t) { + this.counter.addTo(t, 1); + } + + /** + * Decrements the reference counter and removes if count reaches 0. + */ + private void trackReferenceRemoved(Object o) { + if (this.counter.addTo((T) o, -1) <= 1) { + this.counter.removeInt(o); + } + } + + /** + * Factory method to create a HashedReferenceList from an existing list. + */ + public static HashedReferenceList wrapper(List list) { + return new HashedReferenceList<>(list); + } +} From 8f09a8cc43a906e90d2c86e5a1e5c7e796acc758 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 13 May 2025 20:38:35 -0400 Subject: [PATCH 54/81] Fix plugin compatibility --- .../features/0005-Remove-Timings.patch | 77 +++++++------------ 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/leaf-api/paper-patches/features/0005-Remove-Timings.patch b/leaf-api/paper-patches/features/0005-Remove-Timings.patch index 37c32b8a..b03685a1 100644 --- a/leaf-api/paper-patches/features/0005-Remove-Timings.patch +++ b/leaf-api/paper-patches/features/0005-Remove-Timings.patch @@ -3046,59 +3046,31 @@ index 2fae50a9d1f0d9ecd91036697dedd64bc56f7d3b..bf299cfe88c383d489de0c36fd9a4922 return ret; } diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java -deleted file mode 100644 -index 5fbacfcf108432c5187aa9a4092d00d7d5b0fd53..0000000000000000000000000000000000000000 +index 5fbacfcf108432c5187aa9a4092d00d7d5b0fd53..735e8f8591fec1d7108ac020cb2ddcf99577bce1 100644 --- a/src/main/java/org/spigotmc/CustomTimingsHandler.java -+++ /dev/null -@@ -1,67 +0,0 @@ --/* -- * This file is licensed under the MIT License (MIT). -- * -- * Copyright (c) 2014 Daniel Ennis -- * -- * Permission is hereby granted, free of charge, to any person obtaining a copy -- * of this software and associated documentation files (the "Software"), to deal -- * in the Software without restriction, including without limitation the rights -- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -- * copies of the Software, and to permit persons to whom the Software is -- * furnished to do so, subject to the following conditions: -- * -- * The above copyright notice and this permission notice shall be included in -- * all copies or substantial portions of the Software. -- * -- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -- * THE SOFTWARE. -- */ --package org.spigotmc; -- --import org.bukkit.Bukkit; --import org.jetbrains.annotations.NotNull; --import org.bukkit.plugin.AuthorNagException; ++++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java +@@ -26,9 +26,6 @@ package org.spigotmc; + import org.bukkit.Bukkit; + import org.jetbrains.annotations.NotNull; + import org.bukkit.plugin.AuthorNagException; -import co.aikar.timings.Timing; -import co.aikar.timings.Timings; -import co.aikar.timings.TimingsManager; -- --import java.lang.reflect.Method; --import java.util.logging.Level; -- --/** -- * This is here for legacy purposes incase any plugin used it. -- * -- * If you use this, migrate ASAP as this will be removed in the future! -- * -- * @deprecated + + import java.lang.reflect.Method; + import java.util.logging.Level; +@@ -39,29 +36,16 @@ import java.util.logging.Level; + * If you use this, migrate ASAP as this will be removed in the future! + * + * @deprecated - * @see co.aikar.timings.Timings#of -- */ --@Deprecated(forRemoval = true) --public final class CustomTimingsHandler { + */ ++// Leaf start - Remove Timings + @Deprecated(forRemoval = true) + public final class CustomTimingsHandler { - private final Timing handler; -- -- public CustomTimingsHandler(@NotNull String name) { + + public CustomTimingsHandler(@NotNull String name) { - Timing timing; - - new AuthorNagException("Deprecated use of CustomTimingsHandler. Timings has been removed.").printStackTrace(); @@ -3112,12 +3084,15 @@ index 5fbacfcf108432c5187aa9a4092d00d7d5b0fd53..00000000000000000000000000000000 - timing = Timings.NULL_HANDLER; - } - handler = timing; -- } -- + } + - public void startTiming() { handler.startTiming(); } - public void stopTiming() { handler.stopTiming(); } -- --} ++ public void startTiming() {} ++ public void stopTiming() {} ++ // Leaf end - Remove Timings + + } diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java index 37feafd626aaa17aba888d7ff13728b3c6f26d4d..e42619418c1a3e3dac22e7310bb9d64b42b9f6a7 100644 --- a/src/test/java/org/bukkit/AnnotationTest.java From c823590b66833de4e70b707b7bc2907bd0134d96 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 13 May 2025 20:47:03 -0400 Subject: [PATCH 55/81] Updated Upstream (Paper/Gale) Upstream has released updates that appear to apply and compile correctly Paper Changes: PaperMC/Paper@76753219 Allow Server#getDefaultGameMode before worlds are initialized (#12491) PaperMC/Paper@c2bb144f Properly save level data async (#12530) PaperMC/Paper@e2ca4773 Remove simplify remote item matching option for now PaperMC/Paper@af71568b Update mache Gale Changes: Dreeam-qwq/Gale@00ce862b Updated Upstream (Paper) --- gradle.properties | 2 +- .../0166-Save-world-async-properly.patch | 22 +++++++++++++ leaf-server/build.gradle.kts.patch | 2 +- .../0005-Purpur-Server-Paper-Changes.patch | 16 +++++----- .../features/0008-KeYi-Player-Skull-API.patch | 4 +-- .../0009-Slice-Smooth-Teleports.patch | 4 +-- .../features/0011-Leaves-Replay-Mod-API.patch | 4 +-- ...-SparklyPaper-Optimize-canSee-checks.patch | 4 +-- .../GlobalConfiguration.java.patch | 11 ------- .../craftbukkit/entity/CraftPlayer.java.patch | 32 ------------------- 10 files changed, 40 insertions(+), 61 deletions(-) create mode 100644 leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch delete mode 100644 leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch delete mode 100644 leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch diff --git a/gradle.properties b/gradle.properties index a5f55fbf..eca97c93 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=cn.dreeam.leaf mcVersion=1.21.4 version=1.21.4-R0.1-SNAPSHOT -galeCommit=e6d2efb8f8dc092ac55647c745440ea3d60f8797 +galeCommit=00ce862b14008ecbe816606c0843b95d6b227b0d org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch b/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch new file mode 100644 index 00000000..59537340 --- /dev/null +++ b/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 8 May 2025 00:05:01 +0200 +Subject: [PATCH] Save world async properly + +Removed since Paper 1.21.4/5, added on Paper side + +P.S from Tai: I've been using this fix for weeks in my own server but didn't had balls to push it as thought it may cause issues but, it's merged in paper 1.21.5 now. + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index ba1dd51e7187a80e8438e46383257c22f5382130..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1439,7 +1439,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + if (doFull) { +- this.saveLevelData(true); ++ this.saveLevelData(false); + } + // chunk autosave is already called by the ChunkSystem during unload processing (ChunkMap#processUnloads) + // Copied from save() diff --git a/leaf-server/build.gradle.kts.patch b/leaf-server/build.gradle.kts.patch index 4d96a330..f86e1ee5 100644 --- a/leaf-server/build.gradle.kts.patch +++ b/leaf-server/build.gradle.kts.patch @@ -7,7 +7,7 @@ +val leafMavenPublicUrl = "https://maven.nostal.ink/repository/maven-snapshots/" // Leaf - project setup - Add publish repo dependencies { - mache("io.papermc:mache:1.21.4+build.7") + mache("io.papermc:mache:1.21.4+build.8") - paperclip("io.papermc:paperclip:3.0.3") + paperclip("cn.dreeam:quantumleaper:1.0.0-SNAPSHOT") // Leaf - project setup - Use own paperclip fork testRuntimeOnly("org.junit.platform:junit-platform-launcher") diff --git a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch index 52c3af06..385589c1 100644 --- a/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch +++ b/leaf-server/paper-patches/features/0005-Purpur-Server-Paper-Changes.patch @@ -1139,10 +1139,10 @@ index 351f42842b780d053cd2e5bad9ae299449141b10..054d2c2b93c43faeeaf56f482eb7b943 + // Purpur end - Llama API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index cc4b2061ae9cbd5a30b297be477cde703203b3f5..3ba69aa0ac247ecb3406664af606126b30c56ff9 100644 +index 9f195c498a12de07e1581ab13ffe60e77f22a22c..941a92e3b55018d256102ac900f76fae37f8db07 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -589,10 +589,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -588,10 +588,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public void setPlayerListName(String name) { @@ -1159,7 +1159,7 @@ index cc4b2061ae9cbd5a30b297be477cde703203b3f5..3ba69aa0ac247ecb3406664af606126b if (this.getHandle().connection == null) return; // Paper - Updates are possible before the player has fully joined for (ServerPlayer player : (List) this.server.getHandle().players) { if (player.getBukkitEntity().canSee(this)) { -@@ -1431,6 +1436,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -1430,6 +1435,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Paper start - Teleport passenger API // Don't allow teleporting between worlds while keeping passengers if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) { @@ -1167,7 +1167,7 @@ index cc4b2061ae9cbd5a30b297be477cde703203b3f5..3ba69aa0ac247ecb3406664af606126b return false; } -@@ -1452,6 +1458,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -1451,6 +1457,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API @@ -1175,7 +1175,7 @@ index cc4b2061ae9cbd5a30b297be477cde703203b3f5..3ba69aa0ac247ecb3406664af606126b return false; } -@@ -2750,6 +2757,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -2749,6 +2756,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return this.getHandle().getAbilities().walkingSpeed * 2f; } @@ -1204,9 +1204,9 @@ index cc4b2061ae9cbd5a30b297be477cde703203b3f5..3ba69aa0ac247ecb3406664af606126b private void validateSpeed(float value) { Preconditions.checkArgument(value <= 1f && value >= -1f, "Speed value (%s) need to be between -1f and 1f", value); } -@@ -3619,4 +3648,75 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) { - this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck; +@@ -3602,4 +3631,75 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + public void setDeathScreenScore(final int score) { + getHandle().setScore(score); } + + // Purpur start - Purpur client support diff --git a/leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch b/leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch index b2ca3d6e..0405613f 100644 --- a/leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch +++ b/leaf-server/paper-patches/features/0008-KeYi-Player-Skull-API.patch @@ -7,10 +7,10 @@ Original license: MIT Original project: https://github.com/KeYiMC/KeYi diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 3ba69aa0ac247ecb3406664af606126b30c56ff9..e0ab197ef364b7a95e1f5beade5f55c2744b8edc 100644 +index 941a92e3b55018d256102ac900f76fae37f8db07..bb37bd20ec32943932c1a1068a536e17c728e365 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3719,4 +3719,31 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -3702,4 +3702,31 @@ public class CraftPlayer extends CraftHumanEntity implements Player { this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message))); } // Purpur end - Death screen API diff --git a/leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch b/leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch index 511d12b3..50350b10 100644 --- a/leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch +++ b/leaf-server/paper-patches/features/0009-Slice-Smooth-Teleports.patch @@ -9,10 +9,10 @@ Original project: https://github.com/Cryptite/Slice Co-authored-by: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index e0ab197ef364b7a95e1f5beade5f55c2744b8edc..2f027c504859f7ef41ef243bbc16535c6595ec28 100644 +index bb37bd20ec32943932c1a1068a536e17c728e365..9d733dd366d54b0780746a6235d81dc17607cad2 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -1381,6 +1381,25 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -1380,6 +1380,25 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Paper end - Teleportation API } diff --git a/leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch b/leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch index 923206cc..d1c5d644 100644 --- a/leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch +++ b/leaf-server/paper-patches/features/0011-Leaves-Replay-Mod-API.patch @@ -74,10 +74,10 @@ index 8635cd772c5c2ae0ba326812ff2a1a179285a86f..cc024874fbde9678bdddfdca7c250808 if (entity instanceof EnderDragonPart complexPart) { if (complexPart.parentMob instanceof EnderDragon) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 2f027c504859f7ef41ef243bbc16535c6595ec28..abc762829bc0447936ab9e06eabcb42419578585 100644 +index 9d733dd366d54b0780746a6235d81dc17607cad2..7bd58d683ca2534d36510590e0895dce7c46551a 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -2281,7 +2281,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -2280,7 +2280,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public boolean canSee(Player player) { diff --git a/leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch b/leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch index b33c2d01..3ec63a1f 100644 --- a/leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch +++ b/leaf-server/paper-patches/features/0014-SparklyPaper-Optimize-canSee-checks.patch @@ -16,7 +16,7 @@ This seems stupid, but it does seem that it improves the performance a bit, and We also create a "canSee" method tailored for "ChunkMap#updatePlayer()", a method without the equals check (the "updatePlayer()" already checks if the entity is the same entity) because the CraftPlayer's `equals()` check is a *bit* expensive compared to only checking the object's identity, and because the identity has already been check, we don't need to check it twice. diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index abc762829bc0447936ab9e06eabcb42419578585..8ea9b654eb6098477d51ac24ff5be1a33e3055ae 100644 +index 7bd58d683ca2534d36510590e0895dce7c46551a..8e8630db9f74e5952142dba14ec58917c5745287 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -210,7 +210,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @@ -28,7 +28,7 @@ index abc762829bc0447936ab9e06eabcb42419578585..8ea9b654eb6098477d51ac24ff5be1a3 private final Set unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player private static final WeakHashMap> pluginWeakReferences = new WeakHashMap<>(); private int hash = 0; -@@ -2286,9 +2286,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -2285,9 +2285,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public boolean canSee(org.bukkit.entity.Entity entity) { diff --git a/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch b/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch deleted file mode 100644 index 0cab23f7..00000000 --- a/leaf-server/paper-patches/files/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -@@ -185,6 +_,8 @@ - public CompressionFormat compressionFormat = CompressionFormat.ZLIB; - @Comment("This setting controls if equipment should be updated when handling certain player actions.") - public boolean updateEquipmentOnPlayerActions = true; -+ @Comment("Only checks an item's amount and type instead of its full data during inventory desync checks.") -+ public boolean simplifyRemoteItemMatching = false; - - public enum CompressionFormat { - GZIP, diff --git a/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch b/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch deleted file mode 100644 index d96016a3..00000000 --- a/leaf-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -221,6 +_,7 @@ - private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener(); - public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API - private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit -+ private boolean simplifyContainerDesyncCheck = GlobalConfiguration.get().unsupportedSettings.simplifyRemoteItemMatching; - private long lastSaveTime; // Paper - getLastPlayed replacement API - - public CraftPlayer(CraftServer server, ServerPlayer entity) { -@@ -3601,5 +_,21 @@ - @Override - public void setDeathScreenScore(final int score) { - getHandle().setScore(score); -+ } -+ -+ /** -+ * Returns whether container desync checks should skip the full item comparison of remote carried and changed slots -+ * and should instead only check their type and amount. -+ *

-+ * This is useful if the client is not able to produce the same item stack (or as of 1.21.5, its data hashes) as the server. -+ * -+ * @return whether to simplify container desync checks -+ */ -+ public boolean simplifyContainerDesyncCheck() { -+ return simplifyContainerDesyncCheck; -+ } -+ -+ public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) { -+ this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck; - } - } From b0515441aa6dea9f86a07d1a7fc9137a3b5479ae Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Wed, 14 May 2025 18:54:47 +0900 Subject: [PATCH 56/81] fix synchronize carried item --- .../features/0079-Hide-specified-item-components.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch index d0de99b1..5745ed27 100644 --- a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch +++ b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch @@ -33,7 +33,7 @@ index c1130f596cf3443eeb62eb1b12587172fe0859ee..18590e0b1d94ee3266637c5f3ab65ead @Override diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..1e8a6b132926525fad405cbf3a2fab5d32e003e1 100644 +index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..f4c4998a4913358e5164b44eb78b4f3df04778d2 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -306,7 +306,12 @@ public abstract class AbstractContainerMenu { @@ -44,7 +44,7 @@ index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..1e8a6b132926525fad405cbf3a2fab5d + // Leaf start - Hide specified item components - Avoid some frequent client animations + final boolean matchResult = org.dreeam.leaf.config.modules.gameplay.HideItemComponent.enabled + ? !org.dreeam.leaf.util.item.ItemStackStripper.matchesStripped(this.getCarried(), this.remoteCarried) -+ : ItemStack.matches(this.getCarried(), this.remoteCarried); // Paper - add flag to simplify remote matching logic ++ : !ItemStack.matches(this.getCarried(), this.remoteCarried); // Paper - add flag to simplify remote matching logic + if (matchResult) { + // Leaf end - Hide specified item components - Avoid some frequent client animations this.remoteCarried = this.getCarried().copy(); From c940bde695c94fce4f26cafe819a8c351e42424f Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 15 May 2025 10:49:51 +0900 Subject: [PATCH 57/81] optimize async target finding --- .../features/0154-Async-target-finding.patch | 190 ++++++++++++------ .../leaf/async/ai/AsyncGoalExecutor.java | 49 ++--- .../dreeam/leaf/async/ai/AsyncGoalThread.java | 9 +- 3 files changed, 150 insertions(+), 98 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index 5287746c..41961c8e 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -235,7 +235,7 @@ index 9af7dafe03812d96aa477584d4147a68c240ab21..e6fd46b8148e050c4807abf6c8a03e47 // Paper start - log detailed entity tick information diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff339241705d63 100644 +index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd269268800516c426 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -144,6 +144,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -256,7 +256,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 // Paper end - Skip AI during inactive ticks for non-aware mobs boolean isThrottled = org.dreeam.leaf.config.modules.opt.ThrottleInactiveGoalSelectorTick.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking + // Leaf start - Async target finding -+ boolean running = this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null; ++ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); @@ -266,7 +266,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 this.targetSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!running && (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null)) { ++ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } @@ -279,7 +279,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 this.sensing.tick(); int i = this.tickCount + this.getId(); + // Leaf start - Async target finding -+ boolean running = this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null; ++ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; if (i % 2 != 0 && this.tickCount > 1) { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking @@ -296,7 +296,7 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..99362471bee4f8404f7cecd860ff3392 this.goalSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!running && (this.targetSelector.ctxGoals != null || this.goalSelector.ctxGoals != null)) { ++ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { + ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } @@ -667,24 +667,54 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 } else { this.parent = animal; diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538d289b7b2 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..a177505c84697b93d828db9f111bdeb14f57de43 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -26,6 +26,13 @@ public class GoalSelector { +@@ -26,13 +26,23 @@ public class GoalSelector { private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 + // Leaf start - Async target finding -+ public WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; ++ private boolean availableGoalsDirty = true; ++ private WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; + private int ctxIndex = 0; -+ private int ctxState = 0; ++ public int ctxState = -1; + public final org.dreeam.leaf.async.ai.Waker ctx = new org.dreeam.leaf.async.ai.Waker(); + // Leaf end - Async target finding + public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); ++ availableGoalsDirty = true; // Leaf } -@@ -85,7 +92,103 @@ public class GoalSelector { + + @VisibleForTesting + public void removeAllGoals(Predicate filter) { + this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); ++ availableGoalsDirty = true; // Leaf + } + + // Paper start - EAR 2 +@@ -63,16 +73,19 @@ public class GoalSelector { + } + + this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); ++ availableGoalsDirty = true; // Leaf + } + + // Paper start - Perf: optimize goal types + private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet flags) { ++ // Leaf - inline diff + return goal.getFlags().hasCommonElements(flags); + } + + private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map flag) { + long flagIterator = goal.getFlags().getBackingSet(); + int wrappedGoalSize = goal.getFlags().size(); ++ // Leaf - inline diff + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); +@@ -85,7 +98,131 @@ public class GoalSelector { return true; } @@ -718,27 +748,39 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538 + if (ctxState == 1) { + while (ctxIndex < this.ctxGoals.length) { + WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ var flags = goal.getFlags(); + // entity and block -+ if (!goal.isRunning() -+ && !goalContainsAnyFlags(goal, this.goalTypes) -+ && goalCanBeReplacedForAllFlags(goal, this.lockedFlags) -+ ) { -+ if (goal.canUse()) { -+ long flagIterator = goal.getFlags().getBackingSet(); -+ int wrappedGoalSize = goal.getFlags().size(); -+ for (int i = 0; i < wrappedGoalSize; ++i) { -+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; -+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); -+ WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); -+ wrappedGoal1.stop(); -+ this.lockedFlags.put(flag, goal); ++ if (!goal.isRunning() && !flags.hasCommonElements(this.goalTypes)) { ++ // inline ++ boolean result = true; ++ long flagIterator1 = flags.getBackingSet(); ++ int wrappedGoalSize1 = flags.size(); ++ for (int i1 = 0; i1 < wrappedGoalSize1; ++i1) { ++ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator1)]; ++ flagIterator1 ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator1); ++ if (!this.lockedFlags.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { ++ result = false; ++ break; + } -+ -+ goal.start(); + } -+ ctx.state = false; -+ if (ctx.wake != null) { -+ return true; ++ if (result) { ++ if (goal.canUse()) { ++ long flagIterator = flags.getBackingSet(); ++ int wrappedGoalSize = flags.size(); ++ for (int i = 0; i < wrappedGoalSize; ++i) { ++ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); ++ WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); ++ wrappedGoal1.stop(); ++ this.lockedFlags.put(flag, goal); ++ } ++ ++ goal.start(); ++ } ++ ctx.state = false; ++ if (ctx.wake != null) { ++ return true; ++ } + } + } + // entity alert other @@ -766,8 +808,20 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538 + ctxIndex++; + } + -+ ctxGoals = null; -+ ctxState = 0; ++ ctxState = -1; ++ ctxIndex = 0; ++ ctx.state = true; ++ } ++ if (ctxState == 3) { ++ while (ctxIndex < this.ctxGoals.length) { ++ WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ if (goal.isRunning() && goal.requiresUpdateEveryTick()) { ++ goal.tick(); ++ } ++ ctxIndex++; ++ } ++ ++ ctxState = -1; + ctxIndex = 0; + ctx.state = true; + } @@ -778,8 +832,12 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538 public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (this.ctxGoals == null) { -+ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ if (ctxState == -1) { ++ if (availableGoalsDirty || this.ctxGoals == null) { ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ availableGoalsDirty = false; ++ } ++ ctxState = 0; + } + return; + } @@ -788,15 +846,18 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..287b531610327c61fdc505df4ea8e538 for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); -@@ -116,6 +219,15 @@ public class GoalSelector { +@@ -116,6 +253,18 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (this.ctxGoals == null) { -+ this.ctxState = 2; -+ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ if (ctxState == -1) { ++ if (availableGoalsDirty || this.ctxGoals == null) { ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ availableGoalsDirty = false; ++ } ++ ctxState = tickAllRunning ? 2 : 3; + } + return; + } @@ -1427,10 +1488,10 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7ab @Override diff --git a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d7711702e97 100644 +index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617aa32c3cc 100644 --- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -@@ -73,6 +73,49 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -73,6 +73,46 @@ public class HurtByTargetGoal extends TargetGoal { protected void alertOthers() { double followDistance = this.getFollowDistance(); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followDistance, 10.0, followDistance); @@ -1447,27 +1508,24 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d77 + List entitiesOfClass = serverLevel + .getEntitiesOfClass(self.getClass(), aabb, EntitySelector.NO_SPECTATORS); + for (Mob mob : entitiesOfClass) { -+ if (self != mob -+ && mob.getTarget() == null -+ && (!(self instanceof TamableAnimal) || ((TamableAnimal) self).getOwner() == ((TamableAnimal) mob).getOwner()) -+ && !mob.isAlliedTo(self.getLastHurtByMob())) { -+ if (toIgnoreAlert == null) { -+ continue; ++ if (self == mob ++ || mob.getTarget() != null ++ || (self instanceof TamableAnimal && ((TamableAnimal) self).getOwner() != ((TamableAnimal) mob).getOwner()) ++ || mob.isAlliedTo(self.getLastHurtByMob())) { ++ continue; ++ } ++ if (toIgnoreAlert == null) { ++ toAlert.add(mob); ++ continue; ++ } ++ boolean flag = false; ++ for (Class clazz : toIgnoreAlert) { ++ if (mob.getClass() == clazz) { ++ flag = true; ++ break; + } -+ -+ boolean flag = false; -+ -+ for (Class clazz : toIgnoreAlert) { -+ if (mob.getClass() == clazz) { -+ flag = true; -+ break; -+ } -+ } -+ -+ if (!flag) { -+ continue; -+ } -+ ++ } ++ if (!flag) { + toAlert.add(mob); + } + } @@ -1480,7 +1538,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d77 List entitiesOfClass = this.mob .level() .getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); -@@ -87,7 +130,7 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -87,7 +127,7 @@ public class HurtByTargetGoal extends TargetGoal { mob = (Mob)var5.next(); if (this.mob != mob @@ -1489,7 +1547,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d77 && (!(this.mob instanceof TamableAnimal) || ((TamableAnimal)this.mob).getOwner() == ((TamableAnimal)mob).getOwner()) && !mob.isAlliedTo(this.mob.getLastHurtByMob())) { if (this.toIgnoreAlert == null) { -@@ -96,7 +139,7 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -96,7 +136,7 @@ public class HurtByTargetGoal extends TargetGoal { boolean flag = false; @@ -1498,7 +1556,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d77 if (mob.getClass() == clazz) { flag = true; break; -@@ -113,6 +156,30 @@ public class HurtByTargetGoal extends TargetGoal { +@@ -113,6 +153,36 @@ public class HurtByTargetGoal extends TargetGoal { } } @@ -1514,13 +1572,19 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..c11cc768f14a9bf29f40b86c05a37d77 + return; + } + } ++ if (this.mob.getTarget() == null) { ++ return; ++ } ++ if (!canContinueToUse()) { ++ return; ++ } + if (!this.canAttack(lastHurtByMob, HURT_BY_TARGETING)) { + return; + } + for (var obj : toAlert) { + Mob mob = (Mob) obj; -+ if (EntitySelector.NO_SPECTATORS.test(mob) && mob.getTarget() == null && mob.isAlliedTo(this.mob.getLastHurtByMob())) { -+ alertOther(mob, this.mob.getLastHurtByMob()); ++ if (mob.getTarget() == null) { ++ alertOther(mob, lastHurtByMob); + } + } + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index 0f154e37..084ed5df 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -1,5 +1,6 @@ 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; @@ -16,7 +17,7 @@ public class AsyncGoalExecutor { protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); protected final SpscIntQueue queue; protected final SpscIntQueue wake; - protected final SpscIntQueue submit; + protected final IntArrayList submit; private final AsyncGoalThread thread; private final ServerLevel world; private long midTickCount = 0L; @@ -25,7 +26,7 @@ public class AsyncGoalExecutor { this.world = world; this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize); this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize); - this.submit = new SpscIntQueue(AsyncTargetFinding.queueSize); + this.submit = new IntArrayList(); this.thread = thread; } @@ -40,27 +41,29 @@ public class AsyncGoalExecutor { } public final void submit(int entityId) { - if (!this.submit.send(entityId)) { - while (poll(entityId)) { - wake(entityId); - } - } + this.submit.add(entityId); } public final void tick() { - while (true) { - OptionalInt result = this.submit.recv(); - if (result.isEmpty()) { - break; - } - int id = result.getAsInt(); + batchSubmit(); + LockSupport.unpark(thread); + } + + private void batchSubmit() { + if (submit.isEmpty()) { + 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)); } } - LockSupport.unpark(thread); + this.submit.clear(); } public final void midTick() { @@ -77,23 +80,7 @@ public class AsyncGoalExecutor { } } if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) { - boolean submitted = false; - while (true) { - OptionalInt result = this.submit.recv(); - if (result.isEmpty()) { - break; - } - submitted = true; - int id = result.getAsInt(); - if (poll(id) && !this.queue.send(id)) { - do { - wake(id); - } while (poll(id)); - } - } - if (submitted) { - LockSupport.unpark(thread); - } + batchSubmit(); } midTickCount += 1; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java index df3e3a0e..06bd5e48 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalThread.java @@ -35,11 +35,12 @@ public class AsyncGoalThread extends Thread { } } } - } - if (retry) { + Thread.yield(); - } else { - LockSupport.park(); + } + + if (!retry) { + LockSupport.parkNanos(10_000L); } } } From a8e39fa9bcd5122f22091ef65c634a7e17e13608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=91=A9=F0=9D=92=8A=F0=9D=92=92=F0=9D=92=96?= =?UTF-8?q?=F0=9D=92=82=F0=9D=92=95=F0=9D=92=86=F0=9D=92=93=F0=9D=92=8F?= =?UTF-8?q?=F0=9D=92=8A=F0=9D=92=90=F0=9D=92=8F=F0=9D=92=94?= Date: Wed, 14 May 2025 21:41:28 -0500 Subject: [PATCH 58/81] Prevent overriding spark flags (#322) * Prevent overriding spark flags * Null check --- .../java/org/dreeam/leaf/config/LeafConfig.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java index 230e6b77..9bd93774 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java @@ -7,6 +7,7 @@ import net.minecraft.Util; import org.dreeam.leaf.config.modules.misc.SentryDSN; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; @@ -223,6 +224,11 @@ public class LeafConfig { "config/gale-world-defaults.yml" )); + @Nullable String existing = System.getProperty("spark.serverconfigs.extra"); + if (existing != null) { + extraConfigs.addAll(Arrays.asList(existing.split(","))); + } + for (World world : Bukkit.getWorlds()) { extraConfigs.add(world.getWorldFolder().getName() + "/gale-world.yml"); // Gale world config } @@ -230,10 +236,13 @@ public class LeafConfig { return extraConfigs; } - private static String[] buildSparkHiddenPaths() { - return new String[]{ - SentryDSN.sentryDsnConfigPath // Hide Sentry DSN key - }; + private static List buildSparkHiddenPaths() { + @Nullable String existing = System.getProperty("spark.serverconfigs.hiddenpaths"); + + List extraHidden = existing != null ? new ArrayList<>(Arrays.asList(existing.split(","))) : new ArrayList<>(); + extraHidden.add(SentryDSN.sentryDsnConfigPath); // Hide Sentry DSN key + + return extraHidden; } public static void regSparkExtraConfig() { From 2a72c428638d4d7ff3f8e4f0d1c1d4a9b40eb091 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 15 May 2025 00:00:06 -0400 Subject: [PATCH 59/81] [ci skip] cleanup stage 1 --- .../0166-Save-world-async-properly.patch | 2 +- .../features/0085-Multithreaded-Tracker.patch | 32 ++++++++++--------- .../leaf/async/locate/AsyncLocator.java | 7 +++- .../leaf/async/path/AsyncPathProcessor.java | 13 +++++--- .../leaf/async/path/NodeEvaluatorCache.java | 1 - .../leaf/async/path/NodeEvaluatorType.java | 5 ++- .../async/tracker/MultithreadedTracker.java | 8 ++++- .../org/dreeam/leaf/config/LeafConfig.java | 14 ++++---- .../leaf/config/annotations/Experimental.java | 6 +++- .../modules/async/AsyncPathfinding.java | 16 +++++----- .../modules/async/AsyncPlayerDataSave.java | 1 - .../modules/async/AsyncTargetFinding.java | 1 - .../ConfigurableInventoryOverflowEvent.java | 2 +- .../leaf/protocol/DoABarrelRollProtocol.java | 20 ++++++++++-- .../org/dreeam/leaf/protocol/Protocol.java | 7 ++++ .../org/dreeam/leaf/protocol/Protocols.java | 4 ++- .../leaf/util/cache/IterateOutwardsCache.java | 3 +- .../LongList2BlockPosMutableIterable.java | 3 +- .../dreeam/leaf/util/fastBitRadixSort.java | 4 +-- .../leaf/util/list/HashedReferenceList.java | 3 +- 20 files changed, 97 insertions(+), 55 deletions(-) diff --git a/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch b/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch index 59537340..c74c0fa4 100644 --- a/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch +++ b/leaf-archived-patches/removed/hardfork/server/0166-Save-world-async-properly.patch @@ -16,7 +16,7 @@ index ba1dd51e7187a80e8438e46383257c22f5382130..6cb0c14cb7aa243bbee6ca9ba57da4cc if (doFull) { - this.saveLevelData(true); -+ this.saveLevelData(false); ++ this.saveLevelData(false); // Leaf - Save world async properly } // chunk autosave is already called by the ChunkSystem during unload processing (ChunkMap#processUnloads) // Copied from save() diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index a82a8d0e..de9c1a53 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -37,7 +37,7 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e92867b682 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..08873993b3b75db325fe1f2b4ab1de73b3d05be4 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -70,7 +70,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e9 // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,12 +1089,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,11 +1089,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; @@ -80,13 +80,11 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e9 // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; private ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk; ++ public final Object sync = new Object(); // Leaf - Multithreaded tracker -+ public final Object sync = new Object(); // Leaf -+ @Override public final void moonrise$tick(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { - if (chunk == null) { -@@ -1100,8 +1118,41 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1100,8 +1117,41 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -129,7 +127,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e9 final ServerPlayer player = playersRaw[i]; this.updatePlayer(player); } -@@ -1115,6 +1166,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1115,6 +1165,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } } } @@ -138,7 +136,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e9 } @Override -@@ -1176,7 +1229,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1176,7 +1228,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -147,7 +145,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..c9222a1a8ea9f88eadbb3c16925f96e9 serverPlayerConnection.send(packet); } } -@@ -1189,21 +1242,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1241,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { @@ -187,7 +185,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index d8298c7925e3bcea07ead4d438478cc51abcfa16..38314fcb660f6cbb36d60434d24df5b579425eb3 100644 +index d8298c7925e3bcea07ead4d438478cc51abcfa16..be3057119bcbce4a4f72284fa7ba8f60ba43f397 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -110,8 +110,16 @@ public class ServerEntity { @@ -209,11 +207,11 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..38314fcb660f6cbb36d60434d24df5b5 } } ); -@@ -434,6 +442,21 @@ public class ServerEntity { +@@ -434,16 +442,33 @@ public class ServerEntity { if (this.entity instanceof LivingEntity) { Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); -+ // Leaf start ++ // Leaf start - Multithreaded tracker + if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { + synchronized (attributesToSync) { + if (!attributesToSync.isEmpty()) { @@ -231,17 +229,21 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..38314fcb660f6cbb36d60434d24df5b5 if (!attributesToSync.isEmpty()) { // CraftBukkit start - Send scaled max health if (this.entity instanceof ServerPlayer serverPlayer) { -@@ -444,6 +467,8 @@ public class ServerEntity { +- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); ++ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); // Leaf - Multithreaded tracker - diff on change + } + // CraftBukkit end + this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); } attributesToSync.clear(); + } -+ // Leaf end ++ // Leaf end - Multithreaded tracker } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index d6ebc25dc5f04194edde5ad3a1166113e5542a1d..49cbdf014d0626b36eb4c451b6de09508822b7fd 100644 +index 275b640f4536366152f59acf071dd4eba15696c8..a669a59a42f814480879a52d2da5e04c636720de 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2522,7 +2522,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java b/leaf-server/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java index e2d70fe1..c5541c9b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/locate/AsyncLocator.java @@ -12,7 +12,12 @@ import net.minecraft.tags.TagKey; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.structure.Structure; -import java.util.concurrent.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java index af0f16a8..12ae72fe 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java @@ -1,10 +1,8 @@ package org.dreeam.leaf.async.path; import com.google.common.util.concurrent.ThreadFactoryBuilder; - import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.pathfinder.Path; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -12,7 +10,13 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; /** @@ -97,4 +101,5 @@ public class AsyncPathProcessor { final int queueCapacity = org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingQueueSize; return new LinkedBlockingQueue<>(queueCapacity); - }} + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java index c2397543..3d31badb 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorCache.java @@ -2,7 +2,6 @@ package org.dreeam.leaf.async.path; import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; import net.minecraft.world.level.pathfinder.NodeEvaluator; - import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorType.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorType.java index c0527323..c94f4c8b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorType.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/NodeEvaluatorType.java @@ -1,6 +1,9 @@ package org.dreeam.leaf.async.path; -import net.minecraft.world.level.pathfinder.*; +import net.minecraft.world.level.pathfinder.AmphibiousNodeEvaluator; +import net.minecraft.world.level.pathfinder.FlyNodeEvaluator; +import net.minecraft.world.level.pathfinder.NodeEvaluator; +import net.minecraft.world.level.pathfinder.SwimNodeEvaluator; public enum NodeEvaluatorType { WALK, diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index ba41ecdc..23e6171c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -16,7 +16,13 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; public class MultithreadedTracker { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java index 9bd93774..e40a1c50 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/LeafConfig.java @@ -4,10 +4,15 @@ import io.papermc.paper.configuration.GlobalConfiguration; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; 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.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; import java.io.File; import java.io.IOException; @@ -31,13 +36,6 @@ import java.util.concurrent.CompletableFuture; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - /* * Yoinked from: https://github.com/xGinko/AnarchyExploitFixes/ & https://github.com/LuminolMC/Luminol * @author: @xGinko & @MrHua269 diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/annotations/Experimental.java b/leaf-server/src/main/java/org/dreeam/leaf/config/annotations/Experimental.java index 26a6967c..acc8aa7f 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/annotations/Experimental.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/annotations/Experimental.java @@ -1,6 +1,10 @@ package org.dreeam.leaf.config.annotations; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Indicates that a feature is experimental and may be removed or changed in the future. diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java index f24fc20f..003458e5 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java @@ -21,15 +21,15 @@ public class AsyncPathfinding extends ConfigModules { @Override public void onLoaded() { config.addCommentRegionBased(getBasePath() + ".reject-policy", """ - The policy to use when the queue is full and a new task is submitted. - FLUSH_ALL: All pending tasks will be run on server thread. - CALLER_RUNS: Newly submitted task will be run on server thread. - DISCARD: Newly submitted task will be dropped directly.""", + The policy to use when the queue is full and a new task is submitted. + FLUSH_ALL: All pending tasks will be run on server thread. + CALLER_RUNS: Newly submitted task will be run on server thread. + DISCARD: Newly submitted task will be dropped directly.""", """ - 当队列满时, 新提交的任务将使用以下策略处理. - FLUSH_ALL: 所有等待中的任务都将在主线程上运行. - CALLER_RUNS: 新提交的任务将在主线程上运行. - DISCARD: 新提交的任务会被直接丢弃.""" + 当队列满时, 新提交的任务将使用以下策略处理. + FLUSH_ALL: 所有等待中的任务都将在主线程上运行. + CALLER_RUNS: 新提交的任务将在主线程上运行. + DISCARD: 新提交的任务会被直接丢弃.""" ); if (asyncPathfindingInitialized) { config.getConfigSection(getBasePath()); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java index 7b8fecfe..6d555ce0 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPlayerDataSave.java @@ -2,7 +2,6 @@ package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; public class AsyncPlayerDataSave extends ConfigModules { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java index a444f218..c2ebeb42 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -3,7 +3,6 @@ package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; public class AsyncTargetFinding extends ConfigModules { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/ConfigurableInventoryOverflowEvent.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/ConfigurableInventoryOverflowEvent.java index 3338c643..ce78ab95 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/ConfigurableInventoryOverflowEvent.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/ConfigurableInventoryOverflowEvent.java @@ -10,7 +10,7 @@ public class ConfigurableInventoryOverflowEvent extends ConfigModules { } public static boolean enabled = false; - public static String listenerClass = "com.example.package.PlayerInventoryOverflowEvent" ; + public static String listenerClass = "com.example.package.PlayerInventoryOverflowEvent"; @Override public void onLoaded() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java index dca31a84..cdee8b2a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -1,7 +1,14 @@ package org.dreeam.leaf.protocol; import com.google.common.collect.ImmutableList; -import it.unimi.dsi.fastutil.objects.*; +import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; +import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps; +import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2FloatMap; +import it.unimi.dsi.fastutil.objects.Reference2FloatMaps; +import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; @@ -10,9 +17,16 @@ import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.network.ServerPlayerConnection; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.bukkit.event.player.PlayerKickEvent; -import org.dreeam.leaf.protocol.DoABarrelRollPackets.*; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.ConfigResponseC2SPacket; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.ConfigSyncS2CPacket; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.ConfigUpdateAckS2CPacket; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.ConfigUpdateC2SPacket; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.KineticDamage; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.ModConfigServer; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncC2SPacket; +import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncS2CPacket; import org.jetbrains.annotations.NotNull; +import org.bukkit.event.player.PlayerKickEvent; import java.util.List; import java.util.OptionalInt; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java index ac069be5..fdedc410 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java @@ -9,11 +9,18 @@ import java.util.List; interface Protocol { String namespace(); + List> c2s(); + List> s2c(); + void tickServer(MinecraftServer server); + void tickPlayer(ServerPlayer player); + void tickTracker(ServerPlayer player); + void disconnected(ServerPlayer conn); + void handle(ServerPlayer player, @NotNull LeafCustomPayload payload); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java index 0af1e3d1..4a54b998 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java @@ -23,7 +23,9 @@ public class Protocols { PROTOCOLS.remove(protocol); } - public record TypeAndCodec(LeafCustomPayload.Type type, StreamCodec codec) {} + public record TypeAndCodec(LeafCustomPayload.Type type, + StreamCodec codec) { + } public static void write(B byteBuf, LeafCustomPayload payload) { for (Protocol protocol : PROTOCOLS) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java b/leaf-server/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java index 2de80d99..bdd59ee7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/cache/IterateOutwardsCache.java @@ -2,13 +2,12 @@ package org.dreeam.leaf.util.cache; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongList; +import net.minecraft.core.BlockPos; import java.util.Iterator; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.core.BlockPos; - /** * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000 */ diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java b/leaf-server/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java index 53c70ead..5eedd4e7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/cache/LongList2BlockPosMutableIterable.java @@ -2,11 +2,10 @@ package org.dreeam.leaf.util.cache; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongList; +import net.minecraft.core.BlockPos; import java.util.Iterator; -import net.minecraft.core.BlockPos; - /** * @author 2No2Name */ diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java index 21b36d46..ba9cf22c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java @@ -1,9 +1,9 @@ package org.dreeam.leaf.util; -import java.util.List; -import java.util.Arrays; import net.minecraft.world.entity.Entity; + import java.lang.reflect.Array; // Required for Array.newInstance +import java.util.List; public class fastBitRadixSort { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java index 6a66cf99..9887bd89 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java @@ -3,12 +3,13 @@ package org.dreeam.leaf.util.list; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import org.jetbrains.annotations.NotNull; + import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; -import org.jetbrains.annotations.NotNull; /** * A List implementation that maintains a hash-based counter for O(1) element lookup. From b1e74694809c76b3b74f90a97cee029293190547 Mon Sep 17 00:00:00 2001 From: adabugra <57899270+adabugra@users.noreply.github.com> Date: Fri, 16 May 2025 01:30:59 +0300 Subject: [PATCH 60/81] Add configurable death item drop knockback (#326) * feat: add configurable death item drop knockback settings * Add config option xd --- ...e-death-item-drop-knockback-settings.patch | 31 ++++++++++++++++ .../gameplay/DeathItemDropKnockback.java | 36 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/DeathItemDropKnockback.java diff --git a/leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch new file mode 100644 index 00000000..20bcbf59 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: adabugra <57899270+adabugra@users.noreply.github.com> +Date: Thu, 15 May 2025 19:09:04 +0300 +Subject: [PATCH] Add configurable death item drop knockback settings + + +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index 943f48c41214f440517ecf7f392e08f0e4d1b888..52f720aa676e3875546731935c354e851d1cd5c4 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -1096,7 +1096,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + if (!keepInventory) { + for (ItemStack item : this.getInventory().getContents()) { + if (!item.isEmpty() && !EnchantmentHelper.has(item, net.minecraft.world.item.enchantment.EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) { +- loot.add(new DefaultDrop(item, stack -> this.drop(stack, true, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event) ++ loot.add(new DefaultDrop(item, stack -> this.drop(stack, org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.dropAround, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event) + } + } + } +@@ -2849,9 +2849,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + } + + if (dropAround) { +- float f = this.random.nextFloat() * 0.5F; ++ float f = this.random.nextFloat() * (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.horizontalForce; + float f1 = this.random.nextFloat() * (float) (Math.PI * 2); +- itemEntity.setDeltaMovement(-Mth.sin(f1) * f, 0.2F, Mth.cos(f1) * f); ++ itemEntity.setDeltaMovement(-Mth.sin(f1) * f, (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.verticalForce, Mth.cos(f1) * f); + } else { + float f = 0.3F; + float f1 = Mth.sin(this.getXRot() * (float) (Math.PI / 180.0)); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/DeathItemDropKnockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/DeathItemDropKnockback.java new file mode 100644 index 00000000..7807c456 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/DeathItemDropKnockback.java @@ -0,0 +1,36 @@ +package org.dreeam.leaf.config.modules.gameplay; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class DeathItemDropKnockback extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".death-item-drop-knockback"; + } + + public static boolean dropAround = true; + public static double horizontalForce = 0.5; + public static double verticalForce = 0.2; + + @Override + public void onLoaded() { + dropAround = config.getBoolean(getBasePath() + ".drop-around", dropAround, + config.pickStringRegionBased( + "If true, items will drop randomly around the player on death.", + "如果为 “true”,物品会在玩家死亡时随机掉落在其周围." + )); + + horizontalForce = config.getDouble(getBasePath() + ".horizontal-force", horizontalForce, + config.pickStringRegionBased( + "Base speed for horizontal velocity when randomly dropping items.", + "随机掉落物品时水平速度的基本速度." + )); + + verticalForce = config.getDouble(getBasePath() + ".vertical-force", verticalForce, + config.pickStringRegionBased( + "Upward motion for randomly dropped items.", + "随机掉落物品的向上运动." + )); + } +} From a56de1a3b0083d71f98fb8fce64368d279b00bb2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 15 May 2025 21:56:01 -0400 Subject: [PATCH 61/81] [ci skip] cleanup stage 2 --- .../features/0085-Multithreaded-Tracker.patch | 4 ++-- ...t-entities-in-NearestLivingEntitySensor.patch | 16 ++++++++-------- .../features/0132-Async-chunk-send.patch | 4 ++-- ...stBitRadixSort.java => FastBitRadixSort.java} | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) rename leaf-server/src/main/java/org/dreeam/leaf/util/{fastBitRadixSort.java => FastBitRadixSort.java} (98%) diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index de9c1a53..23247266 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -37,7 +37,7 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..08873993b3b75db325fe1f2b4ab1de73b3d05be4 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..4984e57d3b0806164111e79acfc82f6d9b27bfbf 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -88,9 +88,9 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..08873993b3b75db325fe1f2b4ab1de73 this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); ++ // Leaf start - Multithreaded tracker + final int playersLen = players.size(); // Ensure length won't change in the future tasks + -+ // Leaf start - Multithreaded tracker + if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) { + final boolean isServerPlayer = this.entity instanceof ServerPlayer; + final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 1e87e011..87c4d127 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,7 +12,7 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..a887bb8ed2318d6ee39be350a1d0e7223ecf3ff5 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..d4d75a3f5d03533626417aaa3b0457ab98acea1c 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java @@ -13,17 +13,28 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; @@ -21,32 +21,32 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..a887bb8ed2318d6ee39be350a1d0e722 public class NearestLivingEntitySensor extends Sensor { + + // Leaf start - Smart sort entities in NearestLivingEntitySensor -+ private final org.dreeam.leaf.util.fastBitRadixSort sorter; -+ // Leaf end - Smart sort entities in NearestLivingEntitySensor ++ private final org.dreeam.leaf.util.FastBitRadixSort sorter; ++ + public NearestLivingEntitySensor() { -+ this.sorter = new org.dreeam.leaf.util.fastBitRadixSort(); ++ this.sorter = new org.dreeam.leaf.util.FastBitRadixSort(); + } ++ // Leaf end - Smart sort entities in NearestLivingEntitySensor + @Override protected void doTick(ServerLevel level, T entity) { double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE); -+ double rangeSqr = attributeValue * attributeValue; AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); - List entitiesOfClass = level.getEntitiesOfClass( - LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() - ); - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); -+ ++ // Leaf start - Smart sort entities in NearestLivingEntitySensor ++ double rangeSqr = attributeValue * attributeValue; + List entities = level.getEntitiesOfClass(LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr); -+ + LivingEntity[] sorted = this.sorter.sort(entities, entity, LivingEntity.class); + List sortedList = java.util.Arrays.asList(sorted); -+ Brain brain = entity.getBrain(); - brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, entitiesOfClass); - brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, entitiesOfClass)); + brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, sortedList); + brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, sortedList)); ++ // Leaf end - Smart sort entities in NearestLivingEntitySensor } @Override diff --git a/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch b/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch index 196e225f..78fa3b40 100644 --- a/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch +++ b/leaf-server/minecraft-patches/features/0132-Async-chunk-send.patch @@ -158,7 +158,7 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..64a8b50bfac66f75d8c87d9e6e4000dc // Paper start - PlayerChunkLoadEvent if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..e307e618775acb2052593e16d6ff2a5a9edbac4a 100644 +index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..6354a2cbcfe2b940e3c3a80b12b24c7f0f52c202 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -18,7 +18,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ @@ -166,7 +166,7 @@ index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..e307e618775acb2052593e16d6ff2a5a public static final int SECTION_SIZE = 4096; public static final int BIOME_CONTAINER_BITS = 2; - short nonEmptyBlockCount; // Paper - package private -+ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - volatile ++ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - Async chunk send - volatile private short tickingBlockCount; private short tickingFluidCount; private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java b/leaf-server/src/main/java/org/dreeam/leaf/util/FastBitRadixSort.java similarity index 98% rename from leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java rename to leaf-server/src/main/java/org/dreeam/leaf/util/FastBitRadixSort.java index ba9cf22c..598ed57a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/FastBitRadixSort.java @@ -5,7 +5,7 @@ import net.minecraft.world.entity.Entity; import java.lang.reflect.Array; // Required for Array.newInstance import java.util.List; -public class fastBitRadixSort { +public class FastBitRadixSort { private static final int SMALL_ARRAY_THRESHOLD = 2; private Entity[] entityBuffer = new Entity[0]; From 6533fde2d81d2b65065cde0a7da1605957303cc3 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 15 May 2025 23:39:22 -0400 Subject: [PATCH 62/81] [ci skip] cleanup stage 3 --- .../features/0154-Async-target-finding.patch | 54 ++++++++++--------- ...tyList-implementation-to-BasicEntity.patch | 10 ++-- .../features/0164-Protocol-Core.patch | 26 ++++----- .../0165-reduce-PlayerChunk-Updates.patch | 14 ++--- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch index 41961c8e..fd386af4 100644 --- a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch @@ -5,28 +5,30 @@ Subject: [PATCH] Async target finding diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..635d6aabbbe9be5c81a71d5e5bf758211deec0cf 100644 +index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..561723eeb1dd80f7fdd9aff33f6e1c4c16def0ff 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -@@ -40,8 +40,8 @@ public final class ChunkEntitySlices { +@@ -40,8 +40,10 @@ public final class ChunkEntitySlices { private final EntityCollectionBySection allEntities; private final EntityCollectionBySection hardCollidingEntities; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; - private final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByType; -+ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; // Leaf -+ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByType; // Leaf ++ // Leaf start - Async target finding ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByClass; ++ private final Reference2ObjectMap, EntityCollectionBySection> entitiesByType; ++ // Leaf end - Async target finding private final EntityList entities = new EntityList(); public FullChunkStatus status; -@@ -67,9 +67,16 @@ public final class ChunkEntitySlices { +@@ -67,9 +69,15 @@ public final class ChunkEntitySlices { this.allEntities = new EntityCollectionBySection(this); this.hardCollidingEntities = new EntityCollectionBySection(this); - this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); - this.entitiesByType = new Reference2ObjectOpenHashMap<>(); - -+ // Leaf start +- ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + this.entitiesByClass = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); + this.entitiesByType = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); @@ -34,18 +36,18 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..635d6aabbbe9be5c81a71d5e5bf75821 + this.entitiesByClass = new Reference2ObjectOpenHashMap<>(); + this.entitiesByType = new Reference2ObjectOpenHashMap<>(); + } -+ // Leaf end ++ // Leaf end - Async target finding this.status = status; this.chunkData = chunkData; } -@@ -248,14 +255,26 @@ public final class ChunkEntitySlices { +@@ -248,14 +256,26 @@ public final class ChunkEntitySlices { this.hardCollidingEntities.addEntity(entity, sectionIndex); } - for (final Iterator, EntityCollectionBySection>> iterator = - this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); -+ // Leaf start ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this.entitiesByClass) { + for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { @@ -66,18 +68,18 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..635d6aabbbe9be5c81a71d5e5bf75821 + } } } -+ // Leaf end ++ // Leaf end - Async target finding EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); if (byType != null) { -@@ -282,14 +301,27 @@ public final class ChunkEntitySlices { +@@ -282,14 +302,27 @@ public final class ChunkEntitySlices { this.hardCollidingEntities.removeEntity(entity, sectionIndex); } - for (final Iterator, EntityCollectionBySection>> iterator = - this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) { - final Reference2ObjectMap.Entry, EntityCollectionBySection> entry = iterator.next(); -+ // Leaf start ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + synchronized (this.entitiesByClass) { + for (final var entry : this.entitiesByClass.reference2ObjectEntrySet()) { @@ -98,7 +100,7 @@ index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..635d6aabbbe9be5c81a71d5e5bf75821 + } } } -+ // Leaf end ++ // Leaf end - Async target finding + final EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); @@ -667,7 +669,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 } else { this.parent = animal; diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index e82e32407cec6109b9c3b0106295217f4a3f4aa2..a177505c84697b93d828db9f111bdeb14f57de43 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb506310de8461 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -26,13 +26,23 @@ public class GoalSelector { @@ -684,37 +686,39 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..a177505c84697b93d828db9f111bdeb1 + public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); -+ availableGoalsDirty = true; // Leaf ++ availableGoalsDirty = true; // Leaf - Async target finding } @VisibleForTesting public void removeAllGoals(Predicate filter) { this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); -+ availableGoalsDirty = true; // Leaf ++ availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - EAR 2 -@@ -63,16 +73,19 @@ public class GoalSelector { +@@ -63,18 +73,19 @@ public class GoalSelector { } this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); -+ availableGoalsDirty = true; // Leaf ++ availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - Perf: optimize goal types private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet flags) { -+ // Leaf - inline diff - return goal.getFlags().hasCommonElements(flags); +- return goal.getFlags().hasCommonElements(flags); ++ return goal.getFlags().hasCommonElements(flags); // Leaf - Async target finding - inline diff } private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map flag) { long flagIterator = goal.getFlags().getBackingSet(); int wrappedGoalSize = goal.getFlags().size(); -+ // Leaf - inline diff for (int i = 0; i < wrappedGoalSize; ++i) { - final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; +- final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; // Leaf - Async target finding - inline diff flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); -@@ -85,7 +98,131 @@ public class GoalSelector { + // Paper end - Perf: optimize goal types + if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { +@@ -85,7 +96,131 @@ public class GoalSelector { return true; } @@ -846,7 +850,7 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..a177505c84697b93d828db9f111bdeb1 for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); -@@ -116,6 +253,18 @@ public class GoalSelector { +@@ -116,6 +251,18 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch index 3f1d986d..e38ae491 100644 --- a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch +++ b/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d9fc6fdd8 100644 +index 561723eeb1dd80f7fdd9aff33f6e1c4c16def0ff..1acf552d915f72929e1a68d9c769dd8a9026514f 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -@@ -414,6 +414,13 @@ public final class ChunkEntitySlices { +@@ -415,6 +415,13 @@ public final class ChunkEntitySlices { private E[] storage; private int size; @@ -22,7 +22,7 @@ index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d public BasicEntityList() { this(0); -@@ -434,6 +441,7 @@ public final class ChunkEntitySlices { +@@ -435,6 +442,7 @@ public final class ChunkEntitySlices { private void resize() { if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations this.storage = (E[])new Entity[DEFAULT_CAPACITY]; @@ -30,7 +30,7 @@ index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d } else { this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); } -@@ -447,6 +455,7 @@ public final class ChunkEntitySlices { +@@ -448,6 +456,7 @@ public final class ChunkEntitySlices { } else { this.storage[idx] = entity; } @@ -38,7 +38,7 @@ index 635d6aabbbe9be5c81a71d5e5bf758211deec0cf..a45f4eb235a7823fce84948ad556c36d } public int indexOf(final E entity) { -@@ -462,24 +471,32 @@ public final class ChunkEntitySlices { +@@ -463,24 +472,32 @@ public final class ChunkEntitySlices { } public boolean remove(final E entity) { diff --git a/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch index b322df76..a28bedcc 100644 --- a/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch @@ -5,80 +5,80 @@ Subject: [PATCH] Protocol Core diff --git a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java -index 7e19dfe90a63ff26f03b95891dacb7360bba5a3c..d20ffa172227f85b9fd6ac5e2766f6ebd2d07638 100644 +index 7e19dfe90a63ff26f03b95891dacb7360bba5a3c..5d0961f06c23121883c4f0b889ccdf32f06e8a35 100644 --- a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java +++ b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java @@ -47,6 +47,12 @@ public interface CustomPacketPayload { return; } // Leaves end - protocol core -+ // Leaf start - protocol ++ // Leaf start - Protocol core + if (value instanceof org.dreeam.leaf.protocol.LeafCustomPayload payload) { + org.dreeam.leaf.protocol.Protocols.write(buffer, payload); + return; + } -+ // Leaf end - protocol ++ // Leaf end - Protocol core this.writeCap(buffer, value.type(), value); } diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 98af1ad020a003db66d7319f33d43deec315aec5..e04a6db55d936277f2a852374f11d483d79a90ed 100644 +index 98af1ad020a003db66d7319f33d43deec315aec5..9669036e6b7f1830888e48c99acb01d443f4e9f0 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -1839,6 +1839,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop Date: Fri, 16 May 2025 22:43:10 +0200 Subject: [PATCH 63/81] [ci skip] fix build --- .../0171-Smart-sort-items-in-NearestItemSensor.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch index 58bac1dd..3f02bbde 100644 --- a/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch +++ b/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Smart sort items in NearestItemSensor diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -index 09fd13e2d958da8326276c4dadf25bf488aff5ac..70036d073b644ff5f486491486c38db5df643107 100644 +index 09fd13e2d958da8326276c4dadf25bf488aff5ac..a6268791efba64aa21957b7883a5691b5a07cc1a 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java @@ -6,6 +6,7 @@ import java.util.List; @@ -20,8 +20,8 @@ index 09fd13e2d958da8326276c4dadf25bf488aff5ac..70036d073b644ff5f486491486c38db5 private static final long Y_RANGE = 16L; public static final int MAX_DISTANCE_TO_WANTED_ITEM = 32; -+ private final org.dreeam.leaf.util.fastBitRadixSort itemSorter; -+ public NearestItemSensor() {this.itemSorter = new org.dreeam.leaf.util.fastBitRadixSort();} ++ private final org.dreeam.leaf.util.FastBitRadixSort itemSorter; ++ public NearestItemSensor() {this.itemSorter = new org.dreeam.leaf.util.FastBitRadixSort();} @Override public Set> requires() { return ImmutableSet.of(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM); From ff9e4f506bce32a6288f5ce73f111990efffb755 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 17 May 2025 21:10:27 +0900 Subject: [PATCH 64/81] optimize tracker (#323) * optimize tracker * optimize scaledRange * cleanup * fix loop * fix loop * optimize AttributeMap * optimize TrackedEntity#seenBy * revert packDirty * cleanup --- .../features/0085-Multithreaded-Tracker.patch | 393 ++++++++++-------- ...ptimize-addOrUpdateTransientModifier.patch | 4 +- .../features/0164-Protocol-Core.patch | 4 +- .../features/0178-optimize-AttributeMap.patch | 86 ++++ ...9-optimize-getScaledTrackingDistance.patch | 25 ++ ...optimize-SynchedEntityData-packDirty.patch | 27 ++ .../features/0024-Multithreaded-Tracker.patch | 31 ++ .../async/tracker/MultithreadedTracker.java | 59 ++- .../leaf/protocol/DoABarrelRollProtocol.java | 2 +- .../util/map/AttributeInstanceArrayMap.java | 311 ++++++++++++++ 10 files changed, 750 insertions(+), 192 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch create mode 100644 leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch create mode 100644 leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index 23247266..8fa0a7e1 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -37,7 +37,7 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..4984e57d3b0806164111e79acfc82f6d9b27bfbf 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542d50e7a98 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -62,7 +62,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..4984e57d3b0806164111e79acfc82f6d protected void tick() { + // Leaf start - Multithreaded tracker + if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ final ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel level = this.level; ++ final ServerLevel level = this.level; + org.dreeam.leaf.async.tracker.MultithreadedTracker.tick(level); + return; + } @@ -70,97 +70,200 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..4984e57d3b0806164111e79acfc82f6d // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,11 +1089,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,12 +1089,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl -+ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl // Leaf - petal - Multithreaded tracker ++ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl // Leaf - petal - Multithreaded tracker ++ public volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY; // Leaf // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; private ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk; -+ public final Object sync = new Object(); // Leaf - Multithreaded tracker ++ public final Object sync = new Object(); // Leaf - Multithreaded tracker ++ // Leaf start - Multithreaded tracker ++ public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0]; ++ public ServerPlayerConnection[] seenBy() { ++ return seenByArray; ++ } ++ public void seenByUpdated() { ++ this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY); ++ } ++ // Leaf end - Multithreaded tracker ++ @Override public final void moonrise$tick(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { -@@ -1100,8 +1117,41 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (chunk == null) { +@@ -1100,27 +1128,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); -+ // Leaf start - Multithreaded tracker -+ final int playersLen = players.size(); // Ensure length won't change in the future tasks -+ -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled && org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) { -+ final boolean isServerPlayer = this.entity instanceof ServerPlayer; -+ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); -+ Runnable updatePlayerTasks = () -> { -+ for (int i = 0; i < playersLen; ++i) { -+ final ServerPlayer player = playersRaw[i]; -+ this.updatePlayer(player); -+ } - +- - for (int i = 0, len = players.size(); i < len; ++i) { -+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { -+ // need to purge any players possible not in the chunk list -+ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ final ServerPlayer player = conn.getPlayer(); -+ if (!players.contains(player)) { -+ this.removePlayer(player); -+ } -+ } -+ } -+ }; -+ -+ // Only update asynchronously for real player, and sync update for fake players -+ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens -+ // To prevent visible issue with player type NPCs -+ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc. -+ if (isRealPlayer || !isServerPlayer) { -+ org.dreeam.leaf.async.tracker.MultithreadedTracker.getTrackerExecutor().execute(updatePlayerTasks); -+ } else { -+ updatePlayerTasks.run(); -+ } -+ } else { -+ final int playersLength = Math.min(playersRaw.length, players.size()); -+ for (int i = 0; i < playersLength; ++i) { ++ final int playersLength = Math.min(playersRaw.length, players.size()); // Leaf - Multithreaded tracker ++ for (int i = 0; i < playersLength; ++i) { // Leaf - Multithreaded tracker final ServerPlayer player = playersRaw[i]; this.updatePlayer(player); } -@@ -1115,6 +1165,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + + if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { + // need to purge any players possible not in the chunk list +- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { ++ // Leaf start ++ boolean removed = false; ++ for (final ServerPlayerConnection conn : this.seenBy()) { + final ServerPlayer player = conn.getPlayer(); + if (!players.contains(player)) { +- this.removePlayer(player); ++ removed |= this.removePlayerMulti(player); ++ } ++ } ++ if (removed) { ++ this.seenByUpdated(); ++ } ++ // Leaf end ++ } ++ } ++ ++ // Leaf start - Multithreaded tracker ++ public final @Nullable Runnable leafTickCompact(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { ++ if (chunk == null) { ++ this.moonrise$clearPlayers(); ++ return null; ++ } ++ ++ final ca.spottedleaf.moonrise.common.list.ReferenceList players = chunk.getPlayers(ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.VIEW_DISTANCE); ++ ++ if (players == null) { ++ this.moonrise$clearPlayers(); ++ return null; ++ } ++ ++ final long lastChunkUpdate = this.lastChunkUpdate; ++ final long currChunkUpdate = chunk.getUpdateCount(); ++ final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk = this.lastTrackedChunk; ++ this.lastChunkUpdate = currChunkUpdate; ++ this.lastTrackedChunk = chunk; ++ ++ final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); ++ final int playersLen = players.size(); // Ensure length won't change in the future tasks ++ ++ if (!org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled || !org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) { ++ throw new IllegalStateException(); ++ } ++ final boolean isServerPlayer = this.entity instanceof ServerPlayer; ++ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); ++ Runnable updatePlayerTasks = () -> { ++ for (int i = 0; i < playersLen; ++i) { ++ final ServerPlayer player = playersRaw[i]; ++ this.updatePlayer(player); ++ } ++ ++ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { ++ // need to purge any players possible not in the chunk list ++ boolean removed = false; ++ for (final ServerPlayerConnection conn : this.seenBy()) { ++ final ServerPlayer player = conn.getPlayer(); ++ if (!players.contains(player)) { ++ removed |= this.removePlayerMulti(player); ++ } ++ } ++ if (removed) { ++ this.seenByUpdated(); } } ++ }; ++ ++ // Only update asynchronously for real player, and sync update for fake players ++ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens ++ // To prevent visible issue with player type NPCs ++ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc. ++ if (isRealPlayer || !isServerPlayer) { ++ return updatePlayerTasks; ++ } else { ++ updatePlayerTasks.run(); ++ return null; } -+ } -+ // Leaf end - Multithreaded tracker + } ++ // Leaf end - Multithreaded tracker + + @Override + public final void moonrise$removeNonTickThreadPlayers() { + boolean foundToRemove = false; +- for (final ServerPlayerConnection conn : this.seenBy) { ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) { + foundToRemove = true; + break; +@@ -1131,12 +1227,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + return; + } + +- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf + ServerPlayer player = conn.getPlayer(); + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) { +- this.removePlayer(player); ++ this.removePlayerMulti(player); // Leaf + } + } ++ this.seenByUpdated(); // Leaf } @Override -@@ -1176,7 +1228,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1146,10 +1243,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + if (this.seenBy.isEmpty()) { + return; + } +- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf + ServerPlayer player = conn.getPlayer(); +- this.removePlayer(player); ++ this.removePlayerMulti(player); // Leaf + } ++ this.seenByUpdated(); // Leaf + } + + @Override +@@ -1176,7 +1274,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { - for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { -+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) {// Leaf - petal - Multithreaded tracker ++ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker serverPlayerConnection.send(packet); } } -@@ -1189,21 +1241,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1287,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { - for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { -+ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) {// Leaf - petal - Multithreaded tracker ++ for (ServerPlayerConnection serverPlayerConnection : this.seenBy()) { // Leaf - petal - Multithreaded tracker this.serverEntity.removePairing(serverPlayerConnection.getPlayer()); } } ++ // Leaf start ++ public boolean removePlayerMulti(ServerPlayer player) { ++ if (this.seenBy.remove(player.connection)) { ++ this.serverEntity.removePairing(player); ++ return true; ++ } else { ++ return false; ++ } ++ } ++ // Leaf end ++ public void removePlayer(ServerPlayer player) { - org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot + //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // Leaf - petal - Multithreaded tracker - We can remove async too if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); } ++ this.seenByUpdated(); // Leaf } public void updatePlayer(ServerPlayer player) { @@ -171,6 +274,22 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..4984e57d3b0806164111e79acfc82f6d // Paper start - remove allocation of Vec3D here // Vec3 vec3 = player.position().subtract(this.entity.position()); double vec3_dx = player.getX() - this.entity.getX(); +@@ -1231,6 +1342,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // CraftBukkit end + if (flag) { + if (this.seenBy.add(player.connection)) { ++ this.seenByUpdated(); // Leaf + // Paper start - entity tracking events + if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) { + this.serverEntity.addPairing(player); +@@ -1239,6 +1351,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker + } + } else if (this.seenBy.remove(player.connection)) { ++ this.seenByUpdated(); // Leaf + this.serverEntity.removePairing(player); + } + } diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca675930ff2 100644 --- a/net/minecraft/server/level/ServerBossEvent.java @@ -185,10 +304,18 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index d8298c7925e3bcea07ead4d438478cc51abcfa16..be3057119bcbce4a4f72284fa7ba8f60ba43f397 100644 +index d8298c7925e3bcea07ead4d438478cc51abcfa16..e67d87b3043b381d27f75f37e3b7f922e18dcc2d 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -110,8 +110,16 @@ public class ServerEntity { +@@ -70,6 +70,7 @@ public class ServerEntity { + private boolean wasOnGround; + @Nullable + private List> trackedDataValues; ++ public boolean wantSendDirtyEntityData = false; // Leaf - Multithreaded tracker + + // CraftBukkit start + private final Set trackedPlayers; +@@ -110,8 +111,16 @@ public class ServerEntity { .forEach( removedPassenger -> { if (removedPassenger instanceof ServerPlayer serverPlayer1) { @@ -207,41 +334,30 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..be3057119bcbce4a4f72284fa7ba8f60 } } ); -@@ -434,16 +442,33 @@ public class ServerEntity { - - if (this.entity instanceof LivingEntity) { - Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); -+ // Leaf start - Multithreaded tracker -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ synchronized (attributesToSync) { -+ if (!attributesToSync.isEmpty()) { -+ // CraftBukkit start - Send scaled max health -+ if (this.entity instanceof ServerPlayer serverPlayer) { -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -+ } -+ // CraftBukkit end -+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); -+ } -+ -+ attributesToSync.clear(); -+ } -+ } else { - if (!attributesToSync.isEmpty()) { - // CraftBukkit start - Send scaled max health - if (this.entity instanceof ServerPlayer serverPlayer) { -- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); // Leaf - Multithreaded tracker - diff on change - } - // CraftBukkit end - this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); - } - - attributesToSync.clear(); -+ } -+ // Leaf end - Multithreaded tracker - } +@@ -124,7 +133,7 @@ public class ServerEntity { + MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames + MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); + if (savedData != null) { +- for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper ++ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // Leaf + final ServerPlayer serverPlayer = connection.getPlayer(); // Paper + savedData.tickCarriedBy(serverPlayer, item); + Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); +@@ -424,7 +433,13 @@ public class ServerEntity { + return Mth.unpackDegrees(this.lastSentYHeadRot); } +- private void sendDirtyEntityData() { ++ public void sendDirtyEntityData() { // Leaf - public ++ // Leaf start - Multithreaded tracker ++ if (Thread.currentThread() instanceof org.dreeam.leaf.async.tracker.MultithreadedTracker.MultithreadedTrackerThread) { ++ wantSendDirtyEntityData = true; ++ return; ++ } ++ // Leaf end - Multithreaded tracker + SynchedEntityData entityData = this.entity.getEntityData(); + List> list = entityData.packDirty(); + if (list != null) { diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 275b640f4536366152f59acf071dd4eba15696c8..a669a59a42f814480879a52d2da5e04c636720de 100644 --- a/net/minecraft/server/level/ServerLevel.java @@ -277,98 +393,29 @@ index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7e // Paper start - Prevent teleporting dead entities if (this.player.isRemoved()) { LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); -diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index 8013594bb4844e7a8abf28123958e7f632d39341..80c703fe85ec21e4d218823504f4bbf7826b2fa6 100644 ---- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -@@ -24,8 +24,11 @@ public class AttributeInstance { - private final Map> modifiersByOperation = Maps.newEnumMap( - AttributeModifier.Operation.class - ); -- private final Map modifierById = new Object2ObjectArrayMap<>(); -- private final Map permanentModifiers = new Object2ObjectArrayMap<>(); -+ // Leaf start - Multithreaded tracker -+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled; -+ private final Map modifierById = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>(); -+ private final Map permanentModifiers = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>(); -+ // Leaf end - Multithreaded tracker - private double baseValue; - private boolean dirty = true; - private double cachedValue; -@@ -114,7 +117,15 @@ public class AttributeInstance { - } +diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java +index c96f458994818392857642282ec3d492124885da..1caf6846c5057f2c9bcd56ba9f217cc2863cd41b 100644 +--- a/net/minecraft/world/entity/item/PrimedTnt.java ++++ b/net/minecraft/world/entity/item/PrimedTnt.java +@@ -142,12 +142,14 @@ public class PrimedTnt extends Entity implements TraceableEntity { + net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket velocityPacket = new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this); + net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket positionPacket = net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket.teleport(this.getId(), net.minecraft.world.entity.PositionMoveRotation.of(this), java.util.Set.of(), this.onGround); - protected void setDirty() { -- this.dirty = true; -+ // Leaf start - Multithreaded tracker -+ if (multiThreadedTrackingEnabled) { -+ synchronized (this) { -+ this.dirty = true; -+ } -+ } else { -+ this.dirty = true; -+ } -+ // Leaf end - Multithreaded tracker - this.onDirty.accept(this); - } - -@@ -141,6 +152,17 @@ public class AttributeInstance { - } - - public double getValue() { -+ // Leaf start - Multithreaded tracker -+ if (multiThreadedTrackingEnabled) { -+ synchronized (this) { -+ if (this.dirty) { -+ this.cachedValue = this.calculateValue(); -+ this.dirty = false; +- ete.seenBy.stream() +- .filter(viewer -> (viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16) +- .forEach(viewer -> { ++ // Leaf start ++ for (var viewer : ete.seenBy()) { ++ if ((viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16) { + viewer.send(velocityPacket); + viewer.send(positionPacket); +- }); ++ } + } -+ return this.cachedValue; -+ } -+ } -+ // Leaf end - Multithreaded tracker - if (this.dirty) { - this.cachedValue = this.calculateValue(); - this.dirty = false; -diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..d6e7685cb0e9beaa017bcc665f0f1c7c29c2ca5f 100644 ---- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java -+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -19,11 +19,11 @@ import org.slf4j.Logger; - - public class AttributeMap { - private static final Logger LOGGER = LogUtils.getLogger(); -- // Gale start - Lithium - replace AI attributes with optimized collections -- private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); -- private final Set attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -- private final Set attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -- // Gale end - Lithium - replace AI attributes with optimized collections -+ // Leaf start - Multithreaded tracker -+ private final Map, AttributeInstance> attributes; -+ private final Set attributesToSync; -+ private final Set attributesToUpdate; -+ // Leaf end - Multithreaded tracker - private final AttributeSupplier supplier; - private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations - private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables -@@ -37,6 +37,17 @@ public class AttributeMap { - // Purpur end - Ridables - this.supplier = defaultAttributes; - this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations -+ // Leaf start - Multithreaded tracker -+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { -+ this.attributes = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0)); -+ this.attributesToSync = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0)); -+ this.attributesToUpdate = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0)); -+ } else { -+ this.attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); -+ this.attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0); -+ this.attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0); -+ } -+ // Leaf end - Multithreaded tracker - } - - private void onAttributeModified(AttributeInstance instance) { ++ // Leaf end + } + } + // Paper end - Option to prevent TNT from moving in water diff --git a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java b/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java index 724466d14c925704671e510cea1919ee95a2ae02..4426b344677ab9f2753dd2d219921bcb7cf39980 100644 --- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java diff --git a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch b/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch index 4757c107..087595bb 100644 --- a/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch +++ b/leaf-server/minecraft-patches/features/0143-Optimize-addOrUpdateTransientModifier.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index ceff383d565267edd13a6d9006030b8e1f8053e3..7dae9cc18cd6eede8f1b2196b55103428f35382e 100644 +index 8013594bb4844e7a8abf28123958e7f632d39341..7505485c8965e5492a9d68288596178cfe0971ee 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -@@ -88,8 +88,13 @@ public class AttributeInstance { +@@ -85,8 +85,13 @@ public class AttributeInstance { } public void addOrUpdateTransientModifier(AttributeModifier modifier) { diff --git a/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch index a28bedcc..59c3cd8d 100644 --- a/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch @@ -34,10 +34,10 @@ index 98af1ad020a003db66d7319f33d43deec315aec5..9669036e6b7f1830888e48c99acb01d4 for (int i = 0; i < this.tickables.size(); i++) { this.tickables.get(i).run(); diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index be3057119bcbce4a4f72284fa7ba8f60ba43f397..65caf86832b3f33ad87208eb37b44ef2d8a2ec4e 100644 +index e67d87b3043b381d27f75f37e3b7f922e18dcc2d..d64434d808d164ab1201f244058e2964860a590c 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -283,6 +283,7 @@ public class ServerEntity { +@@ -284,6 +284,7 @@ public class ServerEntity { this.entity.hurtMarked = false; this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); } diff --git a/leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch b/leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch new file mode 100644 index 00000000..4c3257d5 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Thu, 15 May 2025 21:11:18 +0900 +Subject: [PATCH] optimize AttributeMap + + +diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java +index f8419dde44ebc7324e783f8bee42132d5ec973c3..1c52a118bad6b4b8abdf2527347ef66888b71f54 100644 +--- a/net/minecraft/world/entity/ai/attributes/Attribute.java ++++ b/net/minecraft/world/entity/ai/attributes/Attribute.java +@@ -16,10 +16,15 @@ public class Attribute { + private boolean syncable; + private final String descriptionId; + private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; ++ // Leaf start - optimize AttributeMap ++ public final int uid; ++ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger(); ++ // Leaf end - optimize AttributeMap + + protected Attribute(String descriptionId, double defaultValue) { + this.defaultValue = defaultValue; + this.descriptionId = descriptionId; ++ this.uid = SIZE.getAndAdd(1); // Leaf - optimize AttributeMap + } + + public double getDefaultValue() { +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb9263944e 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -20,12 +20,12 @@ import org.slf4j.Logger; + public class AttributeMap { + private static final Logger LOGGER = LogUtils.getLogger(); + // Gale start - Lithium - replace AI attributes with optimized collections +- private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); ++ private final Map, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - optimize AttributeMap + private final Set attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + private final Set attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + // Gale end - Lithium - replace AI attributes with optimized collections + private final AttributeSupplier supplier; +- private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations ++ // private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations + private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables + + public AttributeMap(AttributeSupplier supplier) { +@@ -36,7 +36,7 @@ public class AttributeMap { + this.entity = entity; + // Purpur end - Ridables + this.supplier = defaultAttributes; +- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations ++ // this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations + } + + private void onAttributeModified(AttributeInstance instance) { +@@ -60,7 +60,17 @@ public class AttributeMap { + + @Nullable + public AttributeInstance getInstance(Holder attribute) { +- return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways ++ // Leaf start - optimize AttributeMap ++ AttributeInstance v; ++ if ((v = this.attributes.get(attribute)) == null) { ++ AttributeInstance newValue; ++ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) { ++ this.attributes.put(attribute, newValue); ++ return newValue; ++ } ++ } ++ return v; ++ // Leaf end - optimize AttributeMap + } + + public boolean hasAttribute(Holder attribute) { +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java +index 24710041ccbc70e5506d8d89ae34f0141977f209..608edf272735d356215cf0147e65dcef391e1638 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java +@@ -11,7 +11,7 @@ public class AttributeSupplier { + private final Map, AttributeInstance> instances; + + AttributeSupplier(Map, AttributeInstance> instances) { +- this.instances = instances; ++ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - optimize AttributeMap + } + + public AttributeInstance getAttributeInstance(Holder attribute) { diff --git a/leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch new file mode 100644 index 00000000..be9b6476 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Sat, 17 May 2025 19:01:50 +0900 +Subject: [PATCH] optimize getScaledTrackingDistance + + +diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java +index eb0589b203bcf72cd24bb37f2c448c23cb8d6f2b..a1ec070b8620ba34bae9103f19307f42e6ed2a7c 100644 +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -838,7 +838,13 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + + @Override + public int getScaledTrackingDistance(int trackingDistance) { +- return this.getProperties().entityBroadcastRangePercentage * trackingDistance / 100; ++ // Leaf start ++ int p = this.getProperties().entityBroadcastRangePercentage; ++ if (p == 100) { ++ return trackingDistance; ++ } ++ return p * trackingDistance / 100; ++ // Leaf end + } + + @Override diff --git a/leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch new file mode 100644 index 00000000..bbfe075e --- /dev/null +++ b/leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Sat, 17 May 2025 19:03:31 +0900 +Subject: [PATCH] optimize SynchedEntityData#packDirty + + +diff --git a/net/minecraft/network/syncher/SynchedEntityData.java b/net/minecraft/network/syncher/SynchedEntityData.java +index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..546c36d0bc14b8db49245ff162be3dbc4d680da6 100644 +--- a/net/minecraft/network/syncher/SynchedEntityData.java ++++ b/net/minecraft/network/syncher/SynchedEntityData.java +@@ -84,7 +84,15 @@ public class SynchedEntityData { + return null; + } else { + this.isDirty = false; +- List> list = new ArrayList<>(); ++ // Leaf start ++ int cap = 0; ++ for (SynchedEntityData.DataItem dataItem : this.itemsById) { ++ if (dataItem.isDirty()) { ++ cap += 1; ++ } ++ } ++ ArrayList> list = new ArrayList<>(cap); ++ // Leaf end + + for (SynchedEntityData.DataItem dataItem : this.itemsById) { + if (dataItem.isDirty()) { diff --git a/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch b/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch index d968d170..832c367a 100644 --- a/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch +++ b/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch @@ -40,6 +40,37 @@ index 379c2dc1853e45a96dda9b13bf28b7e08f65658a..361f4de9cdf0f7505628a2fed2a3f536 throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); } // Leaves start - skip photographer +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index edcd209798740f31cb302f36d7864a0d8ea1d561..e2444cc9e28dd432bf3351066b1408102decfa0a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -749,7 +749,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + ChunkMap.TrackedEntity entityTracker = world.getChunkSource().chunkMap.entityMap.get(this.getEntityId()); + + if (entityTracker != null) { +- for (ServerPlayerConnection connection : entityTracker.seenBy) { ++ for (ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf + players.add(connection.getPlayer().getBukkitEntity()); + } + } +@@ -1057,7 +1057,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + + // Paper start - resend possibly desynced entity instead of add entity packet +- for (final ServerPlayerConnection connection : entityTracker.seenBy) { ++ for (final ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf + this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer()); + } + // Paper end - resend possibly desynced entity instead of add entity packet +@@ -1245,7 +1245,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + + Set set = new java.util.HashSet<>(tracker.seenBy.size()); +- for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy) { ++ for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy()) { // Leaf + set.add(connection.getPlayer().getBukkitEntity().getPlayer()); + } + return set; diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index e52479f3c888268fd1febeb78e9965af834a8ae9..c2552c3706831f7012b5b449fa43c7d5990056a4 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index 23e6171c..9d78ac41 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -2,12 +2,14 @@ package org.dreeam.leaf.async.tracker; import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; -import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup; import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import net.minecraft.Util; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import org.apache.logging.log4j.LogManager; @@ -49,11 +51,7 @@ public class MultithreadedTracker { } } - public static Executor getTrackerExecutor() { - return TRACKER_EXECUTOR; - } - - public static void tick(ChunkSystemServerLevel level) { + public static void tick(ServerLevel level) { try { if (!org.dreeam.leaf.config.modules.async.MultithreadedTracker.compatModeEnabled) { tickAsync(level); @@ -65,7 +63,7 @@ public class MultithreadedTracker { } } - private static void tickAsync(ChunkSystemServerLevel level) { + private static void tickAsync(ServerLevel level) { final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); @@ -74,6 +72,7 @@ public class MultithreadedTracker { // Move tracking to off-main TRACKER_EXECUTOR.execute(() -> { + ReferenceArrayList sendDirty = new ReferenceArrayList<>(); for (final Entity entity : trackerEntitiesRaw) { if (entity == null) continue; @@ -85,18 +84,26 @@ public class MultithreadedTracker { synchronized (tracker.sync) { tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); tracker.serverEntity.sendChanges(); + if (tracker.serverEntity.wantSendDirtyEntityData) { + tracker.serverEntity.wantSendDirtyEntityData = false; + sendDirty.add(tracker.serverEntity); + } } } + if (!sendDirty.isEmpty()) { + level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData)); + } }); } - private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) { + private static void tickAsyncWithCompatMode(ServerLevel level) { final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); final ReferenceList trackerEntities = entityLookup.trackerEntities; final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length]; + final Runnable[] tickTask = new Runnable[trackerEntitiesRaw.length]; int index = 0; for (final Entity entity : trackerEntitiesRaw) { @@ -106,17 +113,40 @@ public class MultithreadedTracker { if (tracker == null) continue; - tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); - sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array + synchronized (tracker.sync) { + tickTask[index] = tracker.leafTickCompact(nearbyPlayers.getChunk(entity.chunkPosition())); + sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array + } + index++; } // batch submit tasks TRACKER_EXECUTOR.execute(() -> { + for (final Runnable tick : tickTask) { + if (tick == null) continue; + + tick.run(); + } for (final Runnable sendChanges : sendChangesTasks) { if (sendChanges == null) continue; sendChanges.run(); } + ReferenceArrayList sendDirty = new ReferenceArrayList<>(); + for (final Entity entity : trackerEntitiesRaw) { + if (entity == null) continue; + + final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); + + if (tracker == null) continue; + if (tracker.serverEntity.wantSendDirtyEntityData) { + tracker.serverEntity.wantSendDirtyEntityData = false; + sendDirty.add(tracker.serverEntity); + } + } + if (!sendDirty.isEmpty()) { + level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData)); + } }); } @@ -161,10 +191,11 @@ public class MultithreadedTracker { private static @NotNull ThreadFactory getThreadFactory() { return new ThreadFactoryBuilder() - .setThreadFactory(MultithreadedTrackerThread::new) - .setNameFormat(THREAD_PREFIX + " Thread - %d") - .setPriority(Thread.NORM_PRIORITY - 2) - .build(); + .setThreadFactory(MultithreadedTrackerThread::new) + .setNameFormat(THREAD_PREFIX + " Thread - %d") + .setPriority(Thread.NORM_PRIORITY - 2) + .setUncaughtExceptionHandler(Util::onThreadException) + .build(); } private static @NotNull RejectedExecutionHandler getRejectedPolicy() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java index cdee8b2a..48c2a35d 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -151,7 +151,7 @@ public class DoABarrelRollProtocol implements Protocol { } var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll); var packet = Protocols.createPacket(payload); - for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy.toArray(new ServerPlayerConnection[0])) { + for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy()) { if (seenBy instanceof ServerGamePacketListenerImpl conn && getHandshakeState(conn).state == HandshakeState.ACCEPTED) { seenBy.send(packet); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java new file mode 100644 index 00000000..13c865eb --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java @@ -0,0 +1,311 @@ +package org.dreeam.leaf.util.map; + +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.AbstractMap.SimpleEntry; + +/// fast array backend map with O(1) get & put & remove +public class AttributeInstanceArrayMap implements Map, AttributeInstance>, Cloneable { + private int size = 0; + private transient AttributeInstance[] a = new AttributeInstance[32]; + private transient KeySet keys; + private transient Values values; + private transient EntrySet entries; + + public AttributeInstanceArrayMap() { + if (BuiltInRegistries.ATTRIBUTE.size() != 32) { + throw new IllegalStateException("Registered custom attribute"); + } + } + + public AttributeInstanceArrayMap(final @NotNull Map, AttributeInstance> m) { + this(); + for (AttributeInstance e : m.values()) { + setByIndex(e.getAttribute().value().uid, e); + } + } + + private void setByIndex(int index, @Nullable AttributeInstance instance) { + boolean empty = a[index] == null; + if (instance == null) { + if (!empty) { + size--; + a[index] = null; + } + } else { + if (empty) { + size++; + } + a[index] = instance; + } + } + + @Override + public final int size() { + return size; + } + + @Override + public final boolean isEmpty() { + return size == 0; + } + + @Override + public final boolean containsKey(Object key) { + if (key instanceof Holder holder && holder.value() instanceof Attribute attribute) { + int uid = attribute.uid; + return uid >= 0 && uid < a.length && a[uid] != null; + } + return false; + } + + @Override + public final boolean containsValue(Object value) { + for (final AttributeInstance instance : a) { + if (Objects.equals(value, instance)) { + return true; + } + } + return false; + } + + @Override + public final AttributeInstance get(Object key) { + return key instanceof Holder holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null; + } + + @Override + public final AttributeInstance put(@NotNull Holder key, AttributeInstance value) { + int uid = key.value().uid; + AttributeInstance prev = a[uid]; + setByIndex(uid, value); + return prev; + } + + @Override + public final AttributeInstance remove(Object key) { + if (!(key instanceof Holder holder) || !(holder.value() instanceof Attribute attribute)) return null; + int uid = attribute.uid; + AttributeInstance prev = a[uid]; + setByIndex(uid, null); + return prev; + } + + @Override + public final void putAll(@NotNull Map, ? extends AttributeInstance> m) { + m.forEach(this::put); + } + + @Override + public final void clear() { + Arrays.fill(a, null); + size = 0; + } + + @Override + public final @NotNull Set> keySet() { + if (keys == null) { + keys = new KeySet(); + } + return keys; + } + + @Override + public final @NotNull Collection values() { + if (values == null) { + values = new Values(); + } + return values; + } + + @Override + public final @NotNull Set, AttributeInstance>> entrySet() { + if (entries == null) { + entries = new EntrySet(); + } + return entries; + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Map other)) return false; + return entrySet().equals(other.entrySet()); + } + + @Override + public final int hashCode() { + return entrySet().hashCode(); + } + + @Override + public AttributeInstanceArrayMap clone() { + AttributeInstanceArrayMap c; + try { + c = (AttributeInstanceArrayMap) super.clone(); + } catch (CloneNotSupportedException cantHappen) { + throw new InternalError(); + } + c.a = a.clone(); + c.entries = null; + c.keys = null; + c.values = null; + return c; + } + + private final class KeySet extends AbstractSet> { + @Override + public @NotNull Iterator> iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object o) { + return AttributeInstanceArrayMap.this.containsKey(o); + } + } + + private final class KeyIterator implements Iterator> { + private int currentIndex = -1; + private int nextIndex = findNextOccupied(0); + + @Override + public boolean hasNext() { + return nextIndex != -1; + } + + @Override + public Holder next() { + if (!hasNext()) throw new NoSuchElementException(); + currentIndex = nextIndex; + nextIndex = findNextOccupied(nextIndex + 1); + return BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(currentIndex); + } + + @Override + public void remove() { + if (currentIndex == -1) throw new IllegalStateException(); + setByIndex(currentIndex, null); + currentIndex = -1; + } + } + + private final class Values extends AbstractCollection { + @Override + public @NotNull Iterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object o) { + return containsValue(o); + } + } + + private final class ValueIterator implements Iterator { + private int currentIndex = -1; + private int nextIndex = findNextOccupied(0); + + @Override + public boolean hasNext() { + return nextIndex != -1; + } + + @Override + public AttributeInstance next() { + if (!hasNext()) throw new NoSuchElementException(); + currentIndex = nextIndex; + AttributeInstance value = a[nextIndex]; + nextIndex = findNextOccupied(nextIndex + 1); + return value; + } + + @Override + public void remove() { + if (currentIndex == -1) throw new IllegalStateException(); + setByIndex(currentIndex, null); + currentIndex = -1; + } + } + + private final class EntrySet extends AbstractSet, AttributeInstance>> { + @Override + public @NotNull Iterator, AttributeInstance>> iterator() { + return new EntryIterator(); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry e)) { + return false; + } + return Objects.equals(get(e.getKey()), e.getValue()); + } + } + + private final class EntryIterator implements Iterator, AttributeInstance>> { + private int currentIndex = -1; + private int nextIndex = findNextOccupied(0); + + @Override + public boolean hasNext() { + return nextIndex != -1; + } + + @Override + public Entry, AttributeInstance> next() { + if (!hasNext()) throw new NoSuchElementException(); + currentIndex = nextIndex; + Holder key = BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(nextIndex); + AttributeInstance value = a[nextIndex]; + nextIndex = findNextOccupied(nextIndex + 1); + return new SimpleEntry<>(key, value) { + @Override + public AttributeInstance setValue(AttributeInstance newValue) { + AttributeInstance old = put(key, newValue); + super.setValue(newValue); + return old; + } + }; + } + + @Override + public void remove() { + if (currentIndex == -1) { + throw new IllegalStateException(); + } + setByIndex(currentIndex, null); + currentIndex = -1; + } + } + + private int findNextOccupied(int start) { + for (int i = start; i < a.length; i++) { + if (a[i] != null) { + return i; + } + } + return -1; + } +} From 7f3e240bbe0970683c40279a7a65f0fde47503b6 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 17 May 2025 23:53:30 +0200 Subject: [PATCH 65/81] fix poi thingy on PWT --- ...-SparklyPaper-Parallel-world-ticking.patch | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 28e5f12a..122f15f4 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -869,32 +869,54 @@ index 88b07fbb96b20124777889830afa480673629d43..f8bb32840129e57b7799f883cb4570d2 + // Leaf end - SparklyPaper - parallel world ticking mod (prevent clearing portal process) } diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java -index 3614551856c594f3c0cfee984fcf03fad672b007..1a41dd00a1ea4d0587d833c85545baf1b5f660d5 100644 +index 3614551856c594f3c0cfee984fcf03fad672b007..f39e2e4b6a3b18a3fbfbd58c99560b9378b7a031 100644 --- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java +++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java -@@ -46,12 +46,20 @@ public class GoToPotentialJobSite extends Behavior { +@@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.behavior; + import com.google.common.collect.ImmutableMap; + import java.util.Optional; + import net.minecraft.core.BlockPos; ++import net.minecraft.core.SectionPos; + import net.minecraft.core.GlobalPos; + import net.minecraft.network.protocol.game.DebugPackets; + import net.minecraft.server.level.ServerLevel; +@@ -44,14 +45,32 @@ public class GoToPotentialJobSite extends Behavior { + Optional memory = entity.getBrain().getMemory(MemoryModuleType.POTENTIAL_JOB_SITE); + memory.ifPresent(globalPos -> { BlockPos blockPos = globalPos.pos(); - ServerLevel level1 = level.getServer().getLevel(globalPos.dimension()); - if (level1 != null) { +- ServerLevel level1 = level.getServer().getLevel(globalPos.dimension()); +- if (level1 != null) { - PoiManager poiManager = level1.getPoiManager(); - if (poiManager.exists(blockPos, holder -> true)) { - poiManager.release(blockPos); - } -+ // Leaf start - SparklyPaper - parallel world ticking mod (handling for navigating to potential job site cross-dimension) -+ Runnable releasePoiTask = () -> { -+ PoiManager poiManager = level1.getPoiManager(); ++ ServerLevel entityLevel = level; // Villager's current level ++ ServerLevel poiLevel = entityLevel.getServer().getLevel(globalPos.dimension()); // POI's actual level ++ ++ if (poiLevel != null) { ++ Runnable poiOperationsTask = () -> { ++ PoiManager poiManager = poiLevel.getPoiManager(); + if (poiManager.exists(blockPos, holder -> true)) { + poiManager.release(blockPos); + } ++ }; ++ ++ // DebugPackets.sendPoiTicketCountPacket uses the entity's level for its PoiManager context. ++ Runnable debugPacketTask = () -> { ++ DebugPackets.sendPoiTicketCountPacket(entityLevel, blockPos); ++ }; - DebugPackets.sendPoiTicketCountPacket(level, blockPos); -+ DebugPackets.sendPoiTicketCountPacket(level, blockPos); -+ }; -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) -+ level.moonrise$getChunkTaskScheduler().scheduleChunkTask(0, 0, releasePoiTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); -+ else -+ releasePoiTask.run(); -+ // Leaf end - SparklyPaper - parallel world ticking mod (handling for navigating to potential job site cross-dimension) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { // Added curly braces here ++ // Schedule POI operations on the POI's level thread, using POI's chunk coordinates for locality ++ poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(SectionPos.blockToSectionCoord(blockPos.getX()), SectionPos.blockToSectionCoord(blockPos.getZ()), poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); ++ // Schedule debug packet on the entity's level thread, using entity's chunk coordinates for locality ++ entityLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(entity.chunkPosition().x, entity.chunkPosition().z, debugPacketTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); ++ } ++ else { // PWT disabled, run inline on current (entity's) thread ++ poiOperationsTask.run(); // This will use poiLevel's PoiManager but thread checks are permissive ++ debugPacketTask.run(); // This will use entityLevel's PoiManager ++ } } }); entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE); @@ -1015,7 +1037,7 @@ index d212f57c8c0b2086f567fd30237b110203d9e8cb..ed4df82581b5411e54068ccc59ea85a7 } else { Entity entity = owner.teleport( diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 1e8a6b132926525fad405cbf3a2fab5d32e003e1..ea3ddc712676b09298f821fac4145a164bccd7c7 100644 +index f4c4998a4913358e5164b44eb78b4f3df04778d2..cb0a064a41f4f72f4f1a8768b6a1961b903e7708 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -92,8 +92,14 @@ public abstract class AbstractContainerMenu { From 69f1f0d43b016a40d0b531b6a2297bfe84b06e68 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 19 May 2025 18:07:37 +0200 Subject: [PATCH 66/81] if it works dont fix it --- ....patch => 0153-Async-target-finding.patch} | 0 ...ntLong2ReferenceChainedHashTable-wit.patch | 110 - ...imize-ThreadedTicketLevelPropagator.patch} | 2 +- ...EffectUtil-getDigSpeedAmplification.patch} | 0 ...patch => 0156-Optimise-chunkUnloads.patch} | 2 +- ...57-Optimize-BlockEntityType-isValid.patch} | 0 ...t-on-player-join-to-avoid-chunk-loa.patch} | 4 +- ...rPR-Fix-save-load-NaN-Entity-Motion.patch} | 0 ...erPR-Fix-unnecessary-map-data-saves.patch} | 0 ...yList-implementation-to-BasicEntity.patch} | 0 ...heck-inside-blocks-and-traverse-blo.patch} | 0 ...ol-Core.patch => 0163-Protocol-Core.patch} | 0 ... => 0164-reduce-PlayerChunk-Updates.patch} | 0 ... 0165-async-switch-connection-state.patch} | 0 ...timise-BlockEntities-tickersInLevel.patch} | 0 ...e-cactus-can-even-survive-being-pla.patch} | 0 ...0168-flush-location-while-knockback.patch} | 0 ...tch => 0169-Only-tick-items-at-hand.patch} | 2 +- ...art-sort-items-in-NearestItemSensor.patch} | 0 ...171-Optimise-player-movement-checks.patch} | 0 ...=> 0172-Remove-streams-in-MobSensor.patch} | 0 ...73-Remove-streams-in-TemptingSensor.patch} | 0 ...tion.patch => 0174-paw-optimization.patch} | 4 +- ...se-HashedList-on-WeightedRandomList.patch} | 0 ...-death-item-drop-knockback-settings.patch} | 2 +- ...patch => 0177-optimize-AttributeMap.patch} | 0 ...-optimize-getScaledTrackingDistance.patch} | 0 ...ptimize-SynchedEntityData-packDirty.patch} | 0 ...currentLong2ReferenceChainedHashTable.java | 2294 ----------------- 29 files changed, 8 insertions(+), 2412 deletions(-) rename leaf-server/minecraft-patches/features/{0154-Async-target-finding.patch => 0153-Async-target-finding.patch} (100%) delete mode 100644 leaf-server/minecraft-patches/features/0153-Replace-ConcurrentLong2ReferenceChainedHashTable-wit.patch rename leaf-server/minecraft-patches/features/{0155-Optimize-ThreadedTicketLevelPropagator.patch => 0154-Optimize-ThreadedTicketLevelPropagator.patch} (98%) rename leaf-server/minecraft-patches/features/{0156-Optimise-MobEffectUtil-getDigSpeedAmplification.patch => 0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch} (100%) rename leaf-server/minecraft-patches/features/{0157-Optimise-chunkUnloads.patch => 0156-Optimise-chunkUnloads.patch} (99%) rename leaf-server/minecraft-patches/features/{0158-Optimize-BlockEntityType-isValid.patch => 0157-Optimize-BlockEntityType-isValid.patch} (100%) rename leaf-server/minecraft-patches/features/{0159-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch => 0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch} (94%) rename leaf-server/minecraft-patches/features/{0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch => 0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch} (100%) rename leaf-server/minecraft-patches/features/{0161-PaperPR-Fix-unnecessary-map-data-saves.patch => 0160-PaperPR-Fix-unnecessary-map-data-saves.patch} (100%) rename leaf-server/minecraft-patches/features/{0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch => 0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch} (100%) rename leaf-server/minecraft-patches/features/{0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch => 0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch} (100%) rename leaf-server/minecraft-patches/features/{0164-Protocol-Core.patch => 0163-Protocol-Core.patch} (100%) rename leaf-server/minecraft-patches/features/{0165-reduce-PlayerChunk-Updates.patch => 0164-reduce-PlayerChunk-Updates.patch} (100%) rename leaf-server/minecraft-patches/features/{0166-async-switch-connection-state.patch => 0165-async-switch-connection-state.patch} (100%) rename leaf-server/minecraft-patches/features/{0167-Optimise-BlockEntities-tickersInLevel.patch => 0166-Optimise-BlockEntities-tickersInLevel.patch} (100%) rename leaf-server/minecraft-patches/features/{0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch => 0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch} (100%) rename leaf-server/minecraft-patches/features/{0169-flush-location-while-knockback.patch => 0168-flush-location-while-knockback.patch} (100%) rename leaf-server/minecraft-patches/features/{0170-Only-tick-items-at-hand.patch => 0169-Only-tick-items-at-hand.patch} (95%) rename leaf-server/minecraft-patches/features/{0171-Smart-sort-items-in-NearestItemSensor.patch => 0170-Smart-sort-items-in-NearestItemSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0172-Optimise-player-movement-checks.patch => 0171-Optimise-player-movement-checks.patch} (100%) rename leaf-server/minecraft-patches/features/{0173-Remove-streams-in-MobSensor.patch => 0172-Remove-streams-in-MobSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0174-Remove-streams-in-TemptingSensor.patch => 0173-Remove-streams-in-TemptingSensor.patch} (100%) rename leaf-server/minecraft-patches/features/{0175-paw-optimization.patch => 0174-paw-optimization.patch} (98%) rename leaf-server/minecraft-patches/features/{0176-Use-HashedList-on-WeightedRandomList.patch => 0175-Use-HashedList-on-WeightedRandomList.patch} (100%) rename leaf-server/minecraft-patches/features/{0177-Add-configurable-death-item-drop-knockback-settings.patch => 0176-Add-configurable-death-item-drop-knockback-settings.patch} (95%) rename leaf-server/minecraft-patches/features/{0178-optimize-AttributeMap.patch => 0177-optimize-AttributeMap.patch} (100%) rename leaf-server/minecraft-patches/features/{0179-optimize-getScaledTrackingDistance.patch => 0178-optimize-getScaledTrackingDistance.patch} (100%) rename leaf-server/minecraft-patches/features/{0180-optimize-SynchedEntityData-packDirty.patch => 0179-optimize-SynchedEntityData-packDirty.patch} (100%) delete mode 100644 leaf-server/src/main/java/org/dreeam/leaf/util/map/spottedleaf/LeafConcurrentLong2ReferenceChainedHashTable.java diff --git a/leaf-server/minecraft-patches/features/0154-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0154-Async-target-finding.patch rename to leaf-server/minecraft-patches/features/0153-Async-target-finding.patch diff --git a/leaf-server/minecraft-patches/features/0153-Replace-ConcurrentLong2ReferenceChainedHashTable-wit.patch b/leaf-server/minecraft-patches/features/0153-Replace-ConcurrentLong2ReferenceChainedHashTable-wit.patch deleted file mode 100644 index a0b98b40..00000000 --- a/leaf-server/minecraft-patches/features/0153-Replace-ConcurrentLong2ReferenceChainedHashTable-wit.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Sun, 13 Apr 2025 16:15:17 +0200 -Subject: [PATCH] Replace ConcurrentLong2ReferenceChainedHashTable with custom - map - - -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java -index 7eafc5b7cba23d8dec92ecc1050afe3fd8c9e309..9b421f0681fe740520457951b1a1632ada59438a 100644 ---- a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java -@@ -16,7 +16,7 @@ public final class ChunkUnloadQueue { - - public final int coordinateShift; - private final AtomicLong orderGenerator = new AtomicLong(); -- private final ConcurrentLong2ReferenceChainedHashTable unloadSections = new ConcurrentLong2ReferenceChainedHashTable<>(); -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable unloadSections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - - /* - * Note: write operations do not occur in parallel for any given section. -@@ -32,8 +32,10 @@ public final class ChunkUnloadQueue { - public List retrieveForAllRegions() { - final List ret = new ArrayList<>(); - -- for (final Iterator> iterator = this.unloadSections.entryIterator(); iterator.hasNext();) { -- final ConcurrentLong2ReferenceChainedHashTable.TableEntry entry = iterator.next(); -+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map -+ for (final Iterator> iterator = this.unloadSections.entryIterator(); iterator.hasNext(); ) { -+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry entry = iterator.next(); -+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - final long key = entry.getKey(); - final UnloadSection section = entry.getValue(); - final int sectionX = CoordinateUtils.getChunkX(key); -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -index 06ac3537f5655d048d770bb004243f207fad9faa..e399fa14e4a28b4fd93d06c603fd70b6fadeb6b0 100644 ---- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -@@ -71,11 +71,13 @@ public final class ChunkHolderManager { - private static final long PROBE_MARKER = Long.MIN_VALUE + 1; - public final ReentrantAreaLock ticketLockArea; - -- private final ConcurrentLong2ReferenceChainedHashTable>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>(); -- private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>(); -+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable>> tickets = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); -+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - final ChunkUnloadQueue unloadQueue; - -- private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable chunkHolders = org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - private final ServerLevel world; - private final ChunkTaskScheduler taskScheduler; - private long currentTick; -@@ -1422,9 +1424,9 @@ public final class ChunkHolderManager { - final JsonArray allTicketsJson = new JsonArray(); - ret.add("tickets", allTicketsJson); - -- for (final Iterator>>> iterator = this.tickets.entryIterator(); -+ for (final Iterator>>> iterator = this.tickets.entryIterator(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - iterator.hasNext();) { -- final ConcurrentLong2ReferenceChainedHashTable.TableEntry>> coordinateTickets = iterator.next(); -+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry>> coordinateTickets = iterator.next(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - final long coordinate = coordinateTickets.getKey(); - final SortedArraySet> tickets = coordinateTickets.getValue(); - -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java -index 310a8f80debadd64c2d962ebf83b7d0505ce6e42..3a7fad46465cac8d2c1b0933b457f5b075586709 100644 ---- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java -@@ -35,11 +35,11 @@ public abstract class ThreadedTicketLevelPropagator { - } - - private final UpdateQueue updateQueue; -- private final ConcurrentLong2ReferenceChainedHashTable

sections; -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable
sections; // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - - public ThreadedTicketLevelPropagator() { - this.updateQueue = new UpdateQueue(); -- this.sections = new ConcurrentLong2ReferenceChainedHashTable<>(); -+ this.sections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - } - - // must hold ticket lock for: -diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java -index 1487b7d8be435b3fbad2aabd05796965b4775a87..57a702bafb36858979427809f8ae4fc1b001c8d6 100644 ---- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java -+++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java -@@ -740,7 +740,7 @@ public final class StarLightInterface { - - public static final class ServerLightQueue extends LightQueue { - -- private final ConcurrentLong2ReferenceChainedHashTable chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>(); -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable chunkTasks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - - public ServerLightQueue(final StarLightInterface lightInterface) { - super(lightInterface); -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index b1f1b596a597d559aa672a3cb46a03917ad746af..0860a700106e8c1afe58c77150a0f3aee8393fdd 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -72,7 +72,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - @VisibleForDebug - private NaturalSpawner.SpawnState lastSpawnState; - // Paper start -- private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); -+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable fullChunks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map - public int getFullChunksCount() { - return this.fullChunks.size(); - } diff --git a/leaf-server/minecraft-patches/features/0155-Optimize-ThreadedTicketLevelPropagator.patch b/leaf-server/minecraft-patches/features/0154-Optimize-ThreadedTicketLevelPropagator.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0155-Optimize-ThreadedTicketLevelPropagator.patch rename to leaf-server/minecraft-patches/features/0154-Optimize-ThreadedTicketLevelPropagator.patch index 9c5503b2..2b09ff0c 100644 --- a/leaf-server/minecraft-patches/features/0155-Optimize-ThreadedTicketLevelPropagator.patch +++ b/leaf-server/minecraft-patches/features/0154-Optimize-ThreadedTicketLevelPropagator.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimize ThreadedTicketLevelPropagator diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java -index 3a7fad46465cac8d2c1b0933b457f5b075586709..a2d76e6fabf2749a1a9f21fe6bdf6524af8bb9b7 100644 +index 310a8f80debadd64c2d962ebf83b7d0505ce6e42..878f8beb769e87f1de70e7be963e88678a89dd0a 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java @@ -998,6 +998,7 @@ public abstract class ThreadedTicketLevelPropagator { diff --git a/leaf-server/minecraft-patches/features/0156-Optimise-MobEffectUtil-getDigSpeedAmplification.patch b/leaf-server/minecraft-patches/features/0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0156-Optimise-MobEffectUtil-getDigSpeedAmplification.patch rename to leaf-server/minecraft-patches/features/0155-Optimise-MobEffectUtil-getDigSpeedAmplification.patch diff --git a/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch b/leaf-server/minecraft-patches/features/0156-Optimise-chunkUnloads.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch rename to leaf-server/minecraft-patches/features/0156-Optimise-chunkUnloads.patch index 1e8b498c..f2d28496 100644 --- a/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch +++ b/leaf-server/minecraft-patches/features/0156-Optimise-chunkUnloads.patch @@ -159,7 +159,7 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..bed3a64388bb43e47c2ba4e67f7dde5b public static final class SaveState { diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..dc158e981199b507531af810ff9ced3ca717e39e 100644 +index 6354a2cbcfe2b940e3c3a80b12b24c7f0f52c202..82f723ab2e92036c56ce6f09109a9c0e25dc025e 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -24,6 +24,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ diff --git a/leaf-server/minecraft-patches/features/0158-Optimize-BlockEntityType-isValid.patch b/leaf-server/minecraft-patches/features/0157-Optimize-BlockEntityType-isValid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0158-Optimize-BlockEntityType-isValid.patch rename to leaf-server/minecraft-patches/features/0157-Optimize-BlockEntityType-isValid.patch diff --git a/leaf-server/minecraft-patches/features/0159-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch b/leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch similarity index 94% rename from leaf-server/minecraft-patches/features/0159-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch rename to leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch index 077569ed..29a2b6f7 100644 --- a/leaf-server/minecraft-patches/features/0159-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch +++ b/leaf-server/minecraft-patches/features/0158-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch @@ -15,7 +15,7 @@ The delay is currently set to 2 seconds, however, we may want to adjust this bef fixes Paper#9581 diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -index c0f14d2bc30a186511bafddc2e80f29b686887c5..d8040bb5a07546b97f66a4db1f8f25a63c613e17 100644 +index 2fef24acfaceab21aad6be50e6b29701fa460bfb..e0b61e2cde3010f8dcd2cc764814e94ab6907c77 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java @@ -47,6 +47,7 @@ public final class RegionizedPlayerChunkLoader { @@ -27,7 +27,7 @@ index c0f14d2bc30a186511bafddc2e80f29b686887c5..d8040bb5a07546b97f66a4db1f8f25a6 public static final int MIN_VIEW_DISTANCE = 2; public static final int MAX_VIEW_DISTANCE = 32; diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 0b8b4658dbbad1bacc13e97b4fc0cdcea7e36a06..eba382a63e9ed13c4ccbc05c6f2132e03a6b3007 100644 +index 3591de34443069f3f163f8d17df6372c3068611d..842cb6efc8aa7c68b7b9cba144d8540679850f23 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -432,6 +432,13 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch b/leaf-server/minecraft-patches/features/0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0160-PaperPR-Fix-save-load-NaN-Entity-Motion.patch rename to leaf-server/minecraft-patches/features/0159-PaperPR-Fix-save-load-NaN-Entity-Motion.patch diff --git a/leaf-server/minecraft-patches/features/0161-PaperPR-Fix-unnecessary-map-data-saves.patch b/leaf-server/minecraft-patches/features/0160-PaperPR-Fix-unnecessary-map-data-saves.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0161-PaperPR-Fix-unnecessary-map-data-saves.patch rename to leaf-server/minecraft-patches/features/0160-PaperPR-Fix-unnecessary-map-data-saves.patch diff --git a/leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0162-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0161-Sakura-copy-EntityList-implementation-to-BasicEntity.patch diff --git a/leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0163-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0162-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch diff --git a/leaf-server/minecraft-patches/features/0164-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0163-Protocol-Core.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0164-Protocol-Core.patch rename to leaf-server/minecraft-patches/features/0163-Protocol-Core.patch diff --git a/leaf-server/minecraft-patches/features/0165-reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0164-reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0165-reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0164-reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0166-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0165-async-switch-connection-state.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0166-async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0165-async-switch-connection-state.patch diff --git a/leaf-server/minecraft-patches/features/0167-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0167-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch diff --git a/leaf-server/minecraft-patches/features/0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0168-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0169-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0168-flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0169-flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0168-flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0170-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0170-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch index a2b04b38..c2917c5d 100644 --- a/leaf-server/minecraft-patches/features/0170-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Only tick items at hand diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 43c4b8e54842310e48bcdaa991c68ff9571d7249..943f48c41214f440517ecf7f392e08f0e4d1b888 100644 +index 50cf63666071f5d01a85dfc6c6c45c19b05d8ec2..f5afaaa8db28295425ff3c6db73917210eadc3ca 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -888,9 +888,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0171-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0171-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0172-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0171-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0173-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0173-Remove-streams-in-MobSensor.patch rename to leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch diff --git a/leaf-server/minecraft-patches/features/0174-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0174-Remove-streams-in-TemptingSensor.patch rename to leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0175-paw-optimization.patch b/leaf-server/minecraft-patches/features/0174-paw-optimization.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0175-paw-optimization.patch rename to leaf-server/minecraft-patches/features/0174-paw-optimization.patch index d3301e11..48fb02b4 100644 --- a/leaf-server/minecraft-patches/features/0175-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0174-paw-optimization.patch @@ -77,7 +77,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 0860a700106e8c1afe58c77150a0f3aee8393fdd..8e5414becccc921db39e4e6eebeb054d3af94291 100644 +index b1f1b596a597d559aa672a3cb46a03917ad746af..d61da0fbe7f6c181e4084ce60bfe7dab86f361ad 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -504,9 +504,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -94,7 +94,7 @@ index 0860a700106e8c1afe58c77150a0f3aee8393fdd..8e5414becccc921db39e4e6eebeb054d this.tickChunks(l, list); // Gale - Purpur - remove vanilla profiler } finally { diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 539206d55e87ec9968664305caaf475c991fe4d5..6cb0c14cb7aa243bbee6ca9ba57da4cc6eafdfd8 100644 +index e6fd46b8148e050c4807abf6c8a03e4747bc0da2..0d8b71bbe5835187d5dfc1a301b6a01b33237bc1 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1387,13 +1387,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch b/leaf-server/minecraft-patches/features/0175-Use-HashedList-on-WeightedRandomList.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0176-Use-HashedList-on-WeightedRandomList.patch rename to leaf-server/minecraft-patches/features/0175-Use-HashedList-on-WeightedRandomList.patch diff --git a/leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch similarity index 95% rename from leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch rename to leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch index 20bcbf59..4dfe585c 100644 --- a/leaf-server/minecraft-patches/features/0177-Add-configurable-death-item-drop-knockback-settings.patch +++ b/leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add configurable death item drop knockback settings diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 943f48c41214f440517ecf7f392e08f0e4d1b888..52f720aa676e3875546731935c354e851d1cd5c4 100644 +index f5afaaa8db28295425ff3c6db73917210eadc3ca..84d105c714c7cf2c015fb847740e25bb51f6d177 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -1096,7 +1096,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch b/leaf-server/minecraft-patches/features/0177-optimize-AttributeMap.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0178-optimize-AttributeMap.patch rename to leaf-server/minecraft-patches/features/0177-optimize-AttributeMap.patch diff --git a/leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0178-optimize-getScaledTrackingDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0179-optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0178-optimize-getScaledTrackingDistance.patch diff --git a/leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0179-optimize-SynchedEntityData-packDirty.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0180-optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0179-optimize-SynchedEntityData-packDirty.patch diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/spottedleaf/LeafConcurrentLong2ReferenceChainedHashTable.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/spottedleaf/LeafConcurrentLong2ReferenceChainedHashTable.java deleted file mode 100644 index 27698be1..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/spottedleaf/LeafConcurrentLong2ReferenceChainedHashTable.java +++ /dev/null @@ -1,2294 +0,0 @@ -package org.dreeam.leaf.util.map.spottedleaf; - -import ca.spottedleaf.concurrentutil.function.BiLong1Function; -import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; -import ca.spottedleaf.concurrentutil.util.HashUtil; -import ca.spottedleaf.concurrentutil.util.IntegerUtil; -import ca.spottedleaf.concurrentutil.util.ThrowUtil; -import ca.spottedleaf.concurrentutil.util.Validate; - -import java.lang.invoke.VarHandle; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.PrimitiveIterator; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.LongAdder; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.LongConsumer; -import java.util.function.LongFunction; -import java.util.function.Predicate; - -/** - * Optimized concurrent hashtable implementation supporting mapping arbitrary {@code long} keys onto non-null {@code Object} - * values with support for multiple writer and multiple reader threads. Utilizes lock-free read paths, - * optimistic lock-free write attempts, and fine-grained locking during modifications and resizing. - * - *

Happens-before relationship

- *

- * As with {@link ConcurrentMap}, actions in a thread prior to placing an object into this map - * happen-before actions subsequent to the access or removal of that object in another thread. - *

- * - *

Atomicity of functional methods

- *

- * Functional methods (like {@code compute}, {@code merge}, etc.) are performed atomically per key. - * The function provided is guaranteed to be invoked at most once per call under a lock specific to the - * entry's bin. Consequently, invoking other map modification methods on this map from within the function - * can lead to undefined behavior or deadlock. - *

- * - * @param The type of mapped values (must be non-null). - * @see java.util.concurrent.ConcurrentHashMap - */ - -public class LeafConcurrentLong2ReferenceChainedHashTable implements Iterable> { - - // --- Constants --- - - protected static final int DEFAULT_CAPACITY = 16; - protected static final float DEFAULT_LOAD_FACTOR = 0.75f; - /** - * The maximum capacity, used if a higher value is implicitly specified by either - * of the constructors with arguments. MUST be a power of two <= 1<<30. - */ - protected static final int MAXIMUM_CAPACITY = 1 << 30; // 2^30 - - protected static final int THRESHOLD_NO_RESIZE = -1; // Sentinel value: table cannot be resized - protected static final int THRESHOLD_RESIZING = -2; // Sentinel value: table is currently resizing - - // --- Instance Fields --- - - /** - * Tracks the number of mappings, using LongAdder for better high-contention performance. - */ - protected final LongAdder size = new LongAdder(); - - /** - * The load factor for the hash table. - */ - protected final float loadFactor; - - /** - * The hash table array. Elements are accessed using VarHandles. - */ - protected volatile TableEntry[] table; - - /** - * The next size value at which to resize (unless {@code <= 0}). - * Accessed via VarHandle {@link #THRESHOLD_HANDLE}. - */ - protected volatile int threshold; - - // --- VarHandles --- - - protected static final VarHandle THRESHOLD_HANDLE; - - static { - try { - THRESHOLD_HANDLE = ConcurrentUtil.getVarHandle(LeafConcurrentLong2ReferenceChainedHashTable.class, "threshold", int.class); - } catch (Throwable t) { - throw new Error("Failed to initialize VarHandles", t); - } - // Static initialization for TableEntry VarHandles is inside the TableEntry class - } - - // --- Views (lazily initialized) --- - - protected transient Values values; - protected transient EntrySet entrySet; - - // --- Constructors --- - - /** - * Creates a new, empty map with the default initial capacity (16) and load factor (0.75). - */ - public LeafConcurrentLong2ReferenceChainedHashTable() { - this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); - } - - /** - * Creates a new, empty map with the specified initial capacity and load factor. - * - * @param initialCapacity The initial capacity. The implementation performs internal - * sizing to accommodate this many elements. - * @param loadFactor The load factor threshold, used to control resizing. - * @throws IllegalArgumentException if the initial capacity is negative or the load - * factor is non-positive or NaN. - */ - @SuppressWarnings("unchecked") - protected LeafConcurrentLong2ReferenceChainedHashTable(final int initialCapacity, final float loadFactor) { - if (loadFactor <= 0.0f || !Float.isFinite(loadFactor)) { - throw new IllegalArgumentException("Invalid load factor: " + loadFactor); - } - if (initialCapacity < 0) { - throw new IllegalArgumentException("Invalid initial capacity: " + initialCapacity); - } - - final int tableSize = getCapacityFor(initialCapacity); - this.loadFactor = loadFactor; - this.setThresholdPlain(getTargetThreshold(tableSize, loadFactor)); // Use plain set, happens-before established by volatile table write - this.table = (TableEntry[]) new TableEntry[tableSize]; // Volatile write publishes the initial state - } - - /** - * Creates a new, empty map with the specified initial capacity and the default load factor (0.75). - * - * @param capacity The initial capacity. - * @throws IllegalArgumentException if the initial capacity is negative. - */ - public static LeafConcurrentLong2ReferenceChainedHashTable createWithCapacity(final int capacity) { - return createWithCapacity(capacity, DEFAULT_LOAD_FACTOR); - } - - /** - * Creates a new, empty map with the specified initial capacity and load factor. - * - * @param capacity The initial capacity. - * @param loadFactor The load factor threshold. - * @throws IllegalArgumentException if the initial capacity is negative or the load factor is non-positive/NaN. - */ - public static LeafConcurrentLong2ReferenceChainedHashTable createWithCapacity(final int capacity, final float loadFactor) { - return new LeafConcurrentLong2ReferenceChainedHashTable<>(capacity, loadFactor); - } - - /** - * Creates a new, empty map with an initial capacity sufficient to hold the specified number of elements - * without resizing, using the default load factor (0.75). - * - * @param expected The expected number of elements. - * @throws IllegalArgumentException if the expected size is negative. - */ - public static LeafConcurrentLong2ReferenceChainedHashTable createWithExpected(final int expected) { - return createWithExpected(expected, DEFAULT_LOAD_FACTOR); - } - - /** - * Creates a new, empty map with an initial capacity sufficient to hold the specified number of elements - * without resizing, using the specified load factor. - * - * @param expected The expected number of elements. - * @param loadFactor The load factor threshold. - * @throws IllegalArgumentException if the expected size is negative or the load factor is non-positive/NaN. - */ - public static LeafConcurrentLong2ReferenceChainedHashTable createWithExpected(final int expected, final float loadFactor) { - if (expected < 0) { - throw new IllegalArgumentException("Invalid expected size: " + expected); - } - // Calculate initial capacity based on expected size and load factor - final double capacityEstimate = ((double) expected / (double) loadFactor) + 1.0; - final int capacity = (capacityEstimate >= (double) MAXIMUM_CAPACITY) - ? MAXIMUM_CAPACITY - : (int) Math.min(MAXIMUM_CAPACITY, Math.max(DEFAULT_CAPACITY, Math.ceil(capacityEstimate))); - return createWithCapacity(capacity, loadFactor); - } - - // --- Internal Helper Methods --- - - /** - * Calculates the target resize threshold. - */ - protected static int getTargetThreshold(final int capacity, final float loadFactor) { - if (capacity >= MAXIMUM_CAPACITY) { - return THRESHOLD_NO_RESIZE; // Max capacity reached, no more resizing - } - // Calculate threshold, preventing overflow and ensuring it's at least 1 - final double calculatedThreshold = (double) capacity * (double) loadFactor; - if (calculatedThreshold >= (double) MAXIMUM_CAPACITY) { - return MAXIMUM_CAPACITY; // Cap threshold at maximum capacity if calculation exceeds it - } - // Use ceil to ensure threshold is met strictly *after* the size reaches it - return (int) Math.max(1, Math.ceil(calculatedThreshold)); - } - - - /** - * Calculates the power-of-two capacity for a given initial capacity request. - */ - protected static int getCapacityFor(final int requestedCapacity) { - if (requestedCapacity <= 0) { - // Default capacity if non-positive requested, could also throw exception - return DEFAULT_CAPACITY; - } - if (requestedCapacity >= MAXIMUM_CAPACITY) { - return MAXIMUM_CAPACITY; - } - // Round up to the next power of two - return IntegerUtil.roundCeilLog2(Math.max(DEFAULT_CAPACITY, requestedCapacity)); - } - - /** - * Computes the hash code for the key. Uses mixing to spread keys more evenly. - */ - protected static int getHash(final long key) { - return (int) HashUtil.mix(key); // Assumes HashUtil.mix provides good distribution - } - - /** - * Returns the load factor associated with this map. - */ - public final float getLoadFactor() { - return this.loadFactor; - } - - // --- VarHandle Accessors for 'threshold' --- - - protected final int getThresholdAcquire() { - return (int) THRESHOLD_HANDLE.getAcquire(this); - } - - protected final int getThresholdVolatile() { - return (int) THRESHOLD_HANDLE.getVolatile(this); - } - - protected final void setThresholdPlain(final int threshold) { - THRESHOLD_HANDLE.set(this, threshold); - } - - protected final void setThresholdRelease(final int threshold) { - THRESHOLD_HANDLE.setRelease(this, threshold); - } - - protected final void setThresholdVolatile(final int threshold) { - THRESHOLD_HANDLE.setVolatile(this, threshold); - } - - protected final int compareExchangeThresholdVolatile(final int expect, final int update) { - return (int) THRESHOLD_HANDLE.compareAndExchange(this, expect, update); - } - - // --- VarHandle Accessors for 'table' array elements --- - - @SuppressWarnings("unchecked") - protected static TableEntry getAtIndexVolatile(final TableEntry[] table, final int index) { - return (TableEntry) TableEntry.TABLE_ENTRY_ARRAY_HANDLE.getVolatile(table, index); - } - - protected static void setAtIndexRelease(final TableEntry[] table, final int index, final TableEntry value) { - TableEntry.TABLE_ENTRY_ARRAY_HANDLE.setRelease(table, index, value); - } - - protected static void setAtIndexVolatile(final TableEntry[] table, final int index, final TableEntry value) { - TableEntry.TABLE_ENTRY_ARRAY_HANDLE.setVolatile(table, index, value); - } - - @SuppressWarnings("unchecked") - protected static TableEntry compareAndExchangeAtIndexVolatile(final TableEntry[] table, final int index, - final TableEntry expect, final TableEntry update) { - return (TableEntry) TableEntry.TABLE_ENTRY_ARRAY_HANDLE.compareAndExchange(table, index, expect, update); - } - - // --- Core Map Operations --- - - /** - * Retrieves the node associated with the key. This is the core lookup logic. - * It handles concurrent resizes without locking for reads. - * Returns null if the key is not found. - * The returned node's value might be null if it's a placeholder during a compute operation. - */ - protected final TableEntry getNode(final long key) { - final int hash = getHash(key); - TableEntry[] currentTable = this.table; // Volatile read - - outer_loop: - for (;;) { // Loop handles table resizes detected during traversal - final int tableLength = currentTable.length; - if (tableLength == 0) { - // Table might not be initialized yet (race in constructor?), re-read. - currentTable = this.table; - if (currentTable.length == 0) { - // Still not initialized? Should not happen normally. Return null safely. - return null; - } - continue; // Retry with the initialized table - } - - final int index = hash & (tableLength - 1); // Calculate index using mask - TableEntry head = getAtIndexVolatile(currentTable, index); // Volatile read of bin head - - if (head == null) { - return null; // Bin is empty - } - - // Check if the bin head is a resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue outer_loop; // Retry operation with the new table - } - - // Check if the head node itself contains the key - // Reduces chain traversal for head hits - if (head.key == key) { - return head; - } - - // Traverse the linked list (chain) in the bin - // Volatile read is necessary here to observe concurrent modifications (removals/resizes) - TableEntry node = head.getNextVolatile(); - while (node != null) { - if (node.key == key) { - return node; // Key found - } - node = node.getNextVolatile(); // Move to the next node using volatile read - } - - // Key not found in the chain. - // Crucial check: Re-read table reference to see if a resize occurred *during* traversal. - TableEntry[] latestTable = this.table; // Volatile read - if (currentTable != latestTable) { - // Table reference changed, a resize happened. Retry the whole lookup. - currentTable = latestTable; - continue outer_loop; - } - - // Key not found, and table reference is stable since traversal started. - return null; - } - } - - /** - * Helps with resizing or gets the reference to the next table if the current - * bin contains a resize marker. - */ - @SuppressWarnings("unchecked") - private TableEntry[] helpResizeOrGetNextTable(TableEntry[] currentTable, TableEntry resizeMarker) { - // The new table reference is stored in the 'value' field of the resize marker - V markerValue = resizeMarker.getValuePlain(); // Plain read is safe, marker itself is effectively final - if (markerValue instanceof TableEntry[]) { - // Consider adding active resizing help here in a contended scenario - return (TableEntry[]) markerValue; - } - // Fallback: Should not happen if markers are correct. Force retry by re-reading table. - return this.table; - } - - - /** - * Returns the value to which the specified key is mapped, - * or {@code null} if this map contains no mapping for the key. - * - * @param key the key whose associated value is to be returned - * @return the value mapped to the key, or {@code null} if none - */ - public V get(final long key) { - final TableEntry node = this.getNode(key); - // Use volatile read on value to ensure happens-before visibility - return (node == null) ? null : node.getValueVolatile(); - } - - /** - * Returns the value to which the specified key is mapped, or - * {@code defaultValue} if this map contains no mapping for the key. - * - * @param key the key whose associated value is to be returned - * @param defaultValue the default mapping of the key - * @return the value mapped to the key, or {@code defaultValue} if none - */ - public V getOrDefault(final long key, final V defaultValue) { - final TableEntry node = this.getNode(key); - if (node == null) { - return defaultValue; - } - // Use volatile read for visibility. Check for null in case it's a compute placeholder. - final V value = node.getValueVolatile(); - return (value == null) ? defaultValue : value; - } - - /** - * Returns {@code true} if this map contains a mapping for the specified key. - * - * @param key The key whose presence in this map is to be tested - * @return {@code true} if this map contains a mapping for the specified key - */ - public boolean containsKey(final long key) { - final TableEntry node = this.getNode(key); - // Must check value is non-null, as getNode might return a placeholder - return node != null && node.getValueVolatile() != null; // Volatile read for visibility - } - - /** - * Returns {@code true} if this map maps one or more keys to the specified value. - * Note: This operation requires traversing the entire map. - * - * @param value value whose presence in this map is to be tested - * @return {@code true} if this map maps one or more keys to the specified value - * @throws NullPointerException if the specified value is null - */ - public boolean containsValue(final V value) { - Validate.notNull(value, "Value cannot be null"); - // Use an iterator that handles concurrent modifications and resizes safely. - NodeIterator iterator = new NodeIterator<>(this.table, this); - TableEntry node; - while ((node = iterator.findNext()) != null) { // findNext safely iterates through nodes - V nodeValue = node.getValueVolatile(); // Volatile read for visibility - if (value.equals(nodeValue)) { - return true; - } - } - return false; - } - - /** - * Returns the number of key-value mappings in this map. If the - * number of elements exceeds {@code Integer.MAX_VALUE}, returns - * {@code Integer.MAX_VALUE}. - * - * @return the number of key-value mappings in this map - */ - public int size() { - final long ret = this.size.sum(); - // Cap the size at Integer.MAX_VALUE as per ConcurrentMap contract - return (ret >= (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) ret; - } - - /** - * Returns {@code true} if this map contains no key-value mappings. - * - * @return {@code true} if this map contains no key-value mappings - */ - public boolean isEmpty() { - // Check size first for a quick exit, but verify with iteration if size is 0 - // as LongAdder.sum() might be transiently inaccurate. - if (this.size.sum() > 0L) { - return false; - } - // If size reports 0, double-check by looking for any actual node - NodeIterator it = new NodeIterator<>(this.table, this); - return it.findNext() == null; - } - - /** - * Increments the size count and initiates resizing if the threshold is exceeded. - */ - protected final void addSize(final long count) { - this.size.add(count); - int currentThreshold; - do { - currentThreshold = this.getThresholdAcquire(); // Acquire fence for reading threshold - if (currentThreshold <= 0) return; // THRESHOLD_NO_RESIZE or THRESHOLD_RESIZING - - final long currentSum = this.size.sum(); // Get current estimated size - if (currentSum < (long) currentThreshold) { - // Double check threshold hasn't changed due to another thread finishing resize - if (currentThreshold == this.getThresholdVolatile()) return; - continue; // Threshold changed, retry the loop - } - - // Size exceeds threshold, attempt to initiate resize - if (this.compareExchangeThresholdVolatile(currentThreshold, THRESHOLD_RESIZING) == currentThreshold) { - this.resize(currentSum); // Pass estimated size - return; // Resize initiated or completed - } - // CAS failed, another thread initiated resize. Loop might retry. - } while (true); - } - - /** - * Decrements the size count. - */ - protected final void subSize(final long count) { - this.size.add(-count); - // Note: No resize check needed on removal - } - - /** - * Resizes the table to accommodate more entries. Called by the thread - * that successfully sets the threshold to THRESHOLD_RESIZING. - */ - @SuppressWarnings("unchecked") - private void resize(final long estimatedSize) { // estimatedSize might not be perfectly accurate - final TableEntry[] oldTable = this.table; // Volatile read - final int oldCapacity = oldTable.length; - - if (oldCapacity >= MAXIMUM_CAPACITY) { - this.setThresholdVolatile(THRESHOLD_NO_RESIZE); - return; - } - - int newCapacity = oldCapacity << 1; // Double the capacity - if (newCapacity <= oldCapacity || newCapacity > MAXIMUM_CAPACITY) { // Handle overflow or max - newCapacity = MAXIMUM_CAPACITY; - } - if (newCapacity == oldCapacity) { // Already maxed out - this.setThresholdVolatile(THRESHOLD_NO_RESIZE); - return; - } - - final int newThreshold = getTargetThreshold(newCapacity, this.loadFactor); - final TableEntry[] newTable = (TableEntry[]) new TableEntry[newCapacity]; - final TableEntry resizeMarker = new TableEntry<>(0L, (V) newTable, true); // Key irrelevant for marker - - for (int i = 0; i < oldCapacity; ++i) { - TableEntry head = getAtIndexVolatile(oldTable, i); - - if (head == null) { - // Try to CAS marker into empty bin - if (compareAndExchangeAtIndexVolatile(oldTable, i, null, resizeMarker) == null) { - continue; // Marked empty bin - } - // CAS failed, re-read - head = getAtIndexVolatile(oldTable, i); - if (head == null || head.isResizeMarker()) continue; // Still null or handled - } - - if (head.isResizeMarker()) { - continue; // Already processed - } - - // Bin has entries, lock head to transfer chain - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(oldTable, i); - // Re-check after lock - if (currentHead != head) { - i--; // Reprocess index 'i' if head changed while waiting - continue; - } - if (head.isResizeMarker()) { - continue; // Marked while waiting - } - - // Split chain: index 'i' vs 'i + oldCapacity' - TableEntry lowH = null, lowT = null; - TableEntry highH = null, highT = null; - - TableEntry current = head; - while (current != null) { - TableEntry next = current.getNextPlain(); // Plain read inside lock - int hash = getHash(current.key); - - if ((hash & oldCapacity) == 0) { // Low bin (index i) - if (lowT == null) lowH = current; - else lowT.setNextPlain(current); - lowT = current; - } else { // High bin (index i + oldCapacity) - if (highT == null) highH = current; - else highT.setNextPlain(current); - highT = current; - } - current = next; - } - - if (lowT != null) lowT.setNextPlain(null); - if (highT != null) highT.setNextPlain(null); - - // Place chains into new table (volatile writes) - setAtIndexVolatile(newTable, i, lowH); - setAtIndexVolatile(newTable, i + oldCapacity, highH); - - // Mark old bin as processed (release write) - setAtIndexRelease(oldTable, i, resizeMarker); - } // End synchronized - } // End loop over old table bins - - // Finalize: publish new table and threshold - this.table = newTable; - this.setThresholdVolatile(newThreshold); - } - - - /** - * Maps the specified key to the specified value in this table. - * Neither the key nor the value can be null. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key}. - * @throws NullPointerException if the specified value is null - */ - public V put(final long key, final V value) { - Validate.notNull(value, "Value may not be null"); - final int hash = getHash(key); - int sizeDelta; - V oldValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - // Init check - if (tableLength == 0) { - currentTable = this.table; - if (currentTable.length == 0) continue; - } - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - // Case 1: Bin is empty - if (head == null) { - TableEntry newNode = new TableEntry<>(key, value); - if (compareAndExchangeAtIndexVolatile(currentTable, index, null, newNode) == null) { - this.addSize(1L); - return null; // Inserted successfully - } - continue table_loop; // CAS failed, retry - } - - // Case 2: Resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Case 3: Optimistic lock-free update attempt - TableEntry node = head; - while (node != null) { - if (node.key == key) { - V currentVal = node.getValueVolatile(); // Volatile read - if (currentVal == null) break; // Placeholder requires lock - // Try atomic update - if (node.compareAndSetValueVolatile(currentVal, value)) { - return currentVal; // Lock-free success - } - break; // CAS failed, need lock - } - node = node.getNextVolatile(); // Volatile read - } - - // Case 4: Locking path - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - // Re-check state after lock - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; - } - - // Traverse again within lock - TableEntry prev = null; - node = head; - while (node != null) { - if (node.key == key) { - oldValue = node.getValuePlain(); // Plain read in lock - node.setValueVolatile(value); // Volatile write for visibility - sizeDelta = (oldValue == null) ? 1 : 0; // Adjust size if replacing placeholder - break table_loop; // Update done - } - prev = node; - node = node.getNextPlain(); // Plain read in lock - } - - // Key not found, add new node to end of chain - if (prev != null) { - TableEntry newNode = new TableEntry<>(key, value); - prev.setNextRelease(newNode); // Release write to link safely - sizeDelta = 1; - oldValue = null; - } else { - continue table_loop; // Should not happen if head was non-null/non-marker. Retry. - } - } // End synchronized - break table_loop; // Operation completed within lock - } // End table_loop - - if (sizeDelta != 0) { - this.addSize(sizeDelta); - } - return oldValue; - } - - - /** - * If the specified key is not already associated with a value, associates - * it with the given value. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with the specified key, or - * {@code null} if there was no mapping for the key. - * @throws NullPointerException if the specified value is null - */ - public V putIfAbsent(final long key, final V value) { - Validate.notNull(value, "Value may not be null"); - final int hash = getHash(key); - int sizeDelta = 0; - V existingValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) { - currentTable = this.table; - continue; - } - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - // Case 1: Bin is empty - if (head == null) { - TableEntry newNode = new TableEntry<>(key, value); - if (compareAndExchangeAtIndexVolatile(currentTable, index, null, newNode) == null) { - this.addSize(1L); - return null; // Inserted - } - continue table_loop; // CAS failed, retry - } - - // Case 2: Resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Case 3: Lock-free check (optimistic) - TableEntry node = head; - while (node != null) { - if (node.key == key) { - existingValue = node.getValueVolatile(); // Volatile read - if (existingValue != null) { - return existingValue; // Key present with value - } - // Placeholder found, need lock - break; - } - node = node.getNextVolatile(); - } - - - // Case 4: Locking path - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // State changed, retry - } - - TableEntry prev = null; - node = head; - while (node != null) { - if (node.key == key) { - existingValue = node.getValuePlain(); // Plain read in lock - if (existingValue != null) { - break table_loop; // Return existing value - } else { - // Placeholder: update it - node.setValueVolatile(value); // Volatile write - sizeDelta = 1; - existingValue = null; // Return null as per contract - break table_loop; - } - } - prev = node; - node = node.getNextPlain(); // Plain read in lock - } - - // Key not found, add new node - if (prev != null) { - TableEntry newNode = new TableEntry<>(key, value); - prev.setNextRelease(newNode); // Release write - sizeDelta = 1; - existingValue = null; - } else { - continue table_loop; // Should not happen - } - } // End synchronized - break table_loop; - } // End table_loop - - if (sizeDelta != 0) { - this.addSize(sizeDelta); - } - return existingValue; - } - - /** - * Replaces the entry for a key only if currently mapped to some value. - * - * @param key key with which the specified value is associated - * @param value value to be associated with the specified key - * @return the previous value associated with the specified key, or - * {@code null} if there was no mapping for the key. - * @throws NullPointerException if the specified value is null - */ - public V replace(final long key, final V value) { - Validate.notNull(value, "Value may not be null"); - final int hash = getHash(key); - V oldValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return null; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return null; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Try Lock-Free Replace Attempt - TableEntry node = head; - while (node != null) { - if (node.key == key) { - do { // CAS retry loop - oldValue = node.getValueVolatile(); // Volatile read - if (oldValue == null) return null; // Cannot replace placeholder - - if (node.compareAndSetValueVolatile(oldValue, value)) { - return oldValue; // Lock-free success - } - // CAS failed, retry if key still matches - } while (node.key == key); - // Key changed or CAS keeps failing, fall back to lock - break; - } - node = node.getNextVolatile(); - } - - // Locking Path - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - node = head; - while (node != null) { - if (node.key == key) { - oldValue = node.getValuePlain(); // Plain read in lock - if (oldValue != null) { - node.setValueVolatile(value); // Volatile write - return oldValue; - } else { - return null; // Cannot replace placeholder - } - } - node = node.getNextPlain(); // Plain read in lock - } - } // End synchronized - - // Key not found after checks - return null; - } // End table_loop - } - - /** - * Replaces the entry for a key only if currently mapped to a given value. - * - * @param key key with which the specified value is associated - * @param expect value expected to be associated with the specified key - * @param update value to be associated with the specified key - * @return {@code true} if the value was replaced - * @throws NullPointerException if {@code expect} or {@code update} is null - */ - public boolean replace(final long key, final V expect, final V update) { - Validate.notNull(expect, "Expected value may not be null"); - Validate.notNull(update, "Update value may not be null"); - final int hash = getHash(key); - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return false; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return false; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Lock-Free CAS Attempt - TableEntry node = head; - while (node != null) { - if (node.key == key) { - V currentVal = node.getValueVolatile(); // Volatile read - if (!Objects.equals(currentVal, expect)) { - return false; // Value doesn't match - } - // Value matches, try CAS - if (node.compareAndSetValueVolatile(expect, update)) { - return true; // Lock-free success - } - // CAS failed, need lock - break; - } - node = node.getNextVolatile(); - } - - // Locking Path - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - node = head; - while (node != null) { - if (node.key == key) { - V currentVal = node.getValuePlain(); // Plain read in lock - if (Objects.equals(currentVal, expect)) { - node.setValueVolatile(update); // Volatile write - return true; // Replaced successfully - } else { - return false; // Value doesn't match - } - } - node = node.getNextPlain(); // Plain read in lock - } - } // End synchronized - - // Key not found - return false; - } // End table_loop - } - - /** - * Removes the mapping for a key from this map if it is present. - * - * @param key key whose mapping is to be removed from the map - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key} - */ - public V remove(final long key) { - final int hash = getHash(key); - int sizeDelta = 0; - V oldValue = null; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return null; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return null; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Removal needs locking - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - oldValue = node.getValuePlain(); // Plain read in lock - sizeDelta = (oldValue != null) ? -1 : 0; // Decrement if actual mapping - - TableEntry next = node.getNextPlain(); // Plain read - // Update links with release semantics - if (prev == null) { - setAtIndexRelease(currentTable, index, next); // Removed head - } else { - prev.setNextRelease(next); // Removed middle/end - } - break table_loop; // Removed, exit loop - } - prev = node; - node = node.getNextPlain(); // Plain read - } - // Key not found in chain within lock - break table_loop; - } // End synchronized - } // End table_loop - - if (sizeDelta != 0) { - this.subSize(-sizeDelta); // subSize takes positive count - } - return oldValue; - } - - - /** - * Removes the entry for a key only if currently mapped to a given value. - * - * @param key key with which the specified value is associated - * @param expect value expected to be associated with the specified key - * @return {@code true} if the value was removed - */ - public boolean remove(final long key, final V expect) { - final int hash = getHash(key); - int sizeDelta = 0; - boolean removed = false; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return false; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return false; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Removal needs locking - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - V currentVal = node.getValuePlain(); // Plain read in lock - if (Objects.equals(currentVal, expect)) { // Safe comparison - removed = true; - sizeDelta = (currentVal != null) ? -1 : 0; // Decrement if actual value - - TableEntry next = node.getNextPlain(); // Plain read - // Update links with release semantics - if (prev == null) { - setAtIndexRelease(currentTable, index, next); - } else { - prev.setNextRelease(next); - } - } else { - removed = false; // Value didn't match - } - break table_loop; // Key processed - } - prev = node; - node = node.getNextPlain(); // Plain read - } - // Key not found in chain within lock - break table_loop; - } // End synchronized - } // End table_loop - - if (sizeDelta != 0) { - this.subSize(-sizeDelta); - } - return removed; - } - - /** - * Removes the entry for the specified key only if its value satisfies the given predicate. - * - * @param key key whose mapping is to be removed from the map - * @param predicate the predicate to apply to the value associated with the key - * @return the value associated with the key before removal if the predicate was satisfied and the entry was removed, - * otherwise {@code null}. - * @throws NullPointerException if the specified predicate is null - */ - public V removeIf(final long key, final Predicate predicate) { - Validate.notNull(predicate, "Predicate may not be null"); - final int hash = getHash(key); - int sizeDelta = 0; - V oldValue = null; - boolean removed = false; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return null; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return null; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Conditional removal needs locking - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - oldValue = node.getValuePlain(); // Plain read in lock - if (oldValue != null && predicate.test(oldValue)) { // Test non-null value - removed = true; - sizeDelta = -1; - - TableEntry next = node.getNextPlain(); // Plain read - // Update links with release semantics - if (prev == null) { - setAtIndexRelease(currentTable, index, next); - } else { - prev.setNextRelease(next); - } - } else { - removed = false; // Predicate failed or value null - } - break table_loop; // Key processed - } - prev = node; - node = node.getNextPlain(); // Plain read - } - // Key not found in chain within lock - break table_loop; - } // End synchronized - } // End table_loop - - if (sizeDelta != 0) { - this.subSize(-sizeDelta); - } - return removed ? oldValue : null; // Return old value only if removed - } - - // --- Compute Methods --- - - /** - * Attempts to compute a mapping for the specified key and its current mapped value - * (or {@code null} if there is no current mapping). The function is - * applied atomically. - * - * @param key key with which the specified value is to be associated - * @param function the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified function is null - */ - public V compute(final long key, final BiLong1Function function) { - Validate.notNull(function, "Function cannot be null"); - final int hash = getHash(key); - int sizeDelta = 0; - V finalValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) { - currentTable = this.table; - continue; - } - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - // Case 1: Bin is empty. Use placeholder logic. - if (head == null) { - TableEntry placeholder = new TableEntry<>(key, null); // Temp node - V computedValue; - synchronized (placeholder) { // Lock placeholder for atomicity - if (getAtIndexVolatile(currentTable, index) == null) { // Re-check bin - try { - computedValue = function.apply(key, null); // Compute with null old value - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - placeholder.setValuePlain(computedValue); // Set value before CAS - // Attempt to insert the computed node - if (compareAndExchangeAtIndexVolatile(currentTable, index, null, placeholder) == null) { - sizeDelta = 1; - finalValue = computedValue; - break table_loop; // Success - } else { - continue table_loop; // CAS failed, retry - } - } else { - finalValue = null; // Computed null, no mapping - break table_loop; - } - } - } // End synchronized(placeholder) - continue table_loop; // Bin changed, retry - } // End Case 1 (head == null) - - // Case 2: Resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Case 3: Bin not empty. Lock head. - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - // Key found. Compute with existing value. - V oldValue = node.getValuePlain(); // Plain read in lock - V computedValue; - try { - computedValue = function.apply(key, oldValue); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - node.setValueVolatile(computedValue); // Update value (volatile write) - finalValue = computedValue; - sizeDelta = (oldValue == null) ? 1 : 0; // Size change if old was placeholder - } else { - // Remove mapping - finalValue = null; - sizeDelta = (oldValue != null) ? -1 : 0; // Size change only if old was value - TableEntry next = node.getNextPlain(); // Plain read - if (prev == null) setAtIndexRelease(currentTable, index, next); - else prev.setNextRelease(next); - } - break table_loop; // Done - } - prev = node; - node = node.getNextPlain(); // Plain read - } // End while - - // Key not found. Compute with null. - V computedValue; - try { - computedValue = function.apply(key, null); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - // Add new mapping - finalValue = computedValue; - sizeDelta = 1; - TableEntry newNode = new TableEntry<>(key, computedValue); - if (prev != null) prev.setNextRelease(newNode); // Release write - else continue table_loop; // Should not happen - } else { - finalValue = null; - sizeDelta = 0; - } - break table_loop; // Done - } // End synchronized(head) - } // End table_loop - - if (sizeDelta > 0) this.addSize(sizeDelta); - else if (sizeDelta < 0) this.subSize(-sizeDelta); - - return finalValue; - } - - /** - * If the specified key is not already associated with a value, attempts to - * compute its value using the given mapping function and enters it into - * this map unless {@code null}. - * - * @param key key with which the specified value is to be associated - * @param function the function to compute a value - * @return the current (existing or computed) value associated with the specified key, - * or null if the computed value is null - * @throws NullPointerException if the specified function is null - */ - public V computeIfAbsent(final long key, final LongFunction function) { - Validate.notNull(function, "Function cannot be null"); - final int hash = getHash(key); - int sizeDelta = 0; - V finalValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) { - currentTable = this.table; - continue; - } - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - // Case 1: Bin is empty. Use placeholder. - if (head == null) { - TableEntry placeholder = new TableEntry<>(key, null); - V computedValue; - synchronized (placeholder) { - if (getAtIndexVolatile(currentTable, index) == null) { - try { - computedValue = function.apply(key); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - placeholder.setValuePlain(computedValue); - if (compareAndExchangeAtIndexVolatile(currentTable, index, null, placeholder) == null) { - sizeDelta = 1; - finalValue = computedValue; - break table_loop; // Inserted - } else { - continue table_loop; // CAS failed, retry - } - } else { - finalValue = null; // Computed null - break table_loop; - } - } - } // End synchronized(placeholder) - continue table_loop; // Bin changed, retry - } // End Case 1 - - // Case 2: Resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Case 3: Lock-free check if key already exists with value - TableEntry node = head; - while (node != null) { - if (node.key == key) { - V existingValue = node.getValueVolatile(); // Volatile read - if (existingValue != null) { - return existingValue; // Already present - } - break; // Placeholder found, need lock - } - node = node.getNextVolatile(); - } - - // Case 4: Locking path - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - node = head; - while (node != null) { - if (node.key == key) { - V existingValue = node.getValuePlain(); // Plain read in lock - if (existingValue != null) { - finalValue = existingValue; // Found inside lock - } else { - // Placeholder exists, compute and update - V computedValue; - try { - computedValue = function.apply(key); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - node.setValueVolatile(computedValue); // Volatile write - sizeDelta = 1; - finalValue = computedValue; - } else { - finalValue = null; // Computed null - } - } - break table_loop; // Done - } - prev = node; - node = node.getNextPlain(); // Plain read - } // End while - - // Key not found. Compute and add. - V computedValue; - try { - computedValue = function.apply(key); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - finalValue = computedValue; - sizeDelta = 1; - TableEntry newNode = new TableEntry<>(key, computedValue); - if (prev != null) prev.setNextRelease(newNode); // Release write - else continue table_loop; // Should not happen - } else { - finalValue = null; - sizeDelta = 0; - } - break table_loop; // Done - } // End synchronized(head) - } // End table_loop - - if (sizeDelta > 0) this.addSize(sizeDelta); - return finalValue; - } - - - /** - * If the value for the specified key is present, attempts to compute a new - * mapping given the key and its current mapped value. - * - * @param key key with which the specified value is to be associated - * @param function the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified function is null - */ - public V computeIfPresent(final long key, final BiLong1Function function) { - Validate.notNull(function, "Function cannot be null"); - final int hash = getHash(key); - int sizeDelta; - V finalValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) return null; - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - if (head == null) return null; - - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Needs lock for potential removal - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - V oldValue = node.getValuePlain(); // Plain read in lock - if (oldValue != null) { // Only compute if value present - V computedValue; - try { - computedValue = function.apply(key, oldValue); - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - - if (computedValue != null) { - node.setValueVolatile(computedValue); // Update (volatile write) - finalValue = computedValue; - sizeDelta = 0; - } else { - // Remove mapping - finalValue = null; - sizeDelta = -1; - TableEntry next = node.getNextPlain(); // Plain read - if (prev == null) setAtIndexRelease(currentTable, index, next); - else prev.setNextRelease(next); - } - } else { - // Placeholder, treat as absent - finalValue = null; - sizeDelta = 0; - } - break table_loop; // Done - } - prev = node; - node = node.getNextPlain(); // Plain read - } // End while - - // Key not found - finalValue = null; - sizeDelta = 0; - break table_loop; - } // End synchronized(head) - } // End table_loop - - if (sizeDelta < 0) this.subSize(-sizeDelta); - return finalValue; - } - - /** - * If the specified key is not already associated with a value or is - * associated with null, associates it with the given non-null value. - * Otherwise, replaces the associated value with the results of the given - * remapping function, or removes if the result is {@code null}. - * - * @param key key with which the resulting value is to be associated - * @param value the non-null value to be merged with the existing value - * @param function the function to recompute a value if present - * @return the new value associated with the specified key, or null if no - * value is associated with the key - * @throws NullPointerException if the specified value or function is null - */ - public V merge(final long key, final V value, final BiFunction function) { - Validate.notNull(value, "Value cannot be null"); - Validate.notNull(function, "Function cannot be null"); - final int hash = getHash(key); - int sizeDelta; - V finalValue; - TableEntry[] currentTable = this.table; - - table_loop: - for (;;) { - final int tableLength = currentTable.length; - if (tableLength == 0) { - currentTable = this.table; - continue; - } - - final int index = hash & (tableLength - 1); - TableEntry head = getAtIndexVolatile(currentTable, index); - - // Case 1: Bin empty. Insert value. - if (head == null) { - TableEntry newNode = new TableEntry<>(key, value); - if (compareAndExchangeAtIndexVolatile(currentTable, index, null, newNode) == null) { - sizeDelta = 1; - finalValue = value; - break table_loop; // Inserted - } - continue table_loop; // CAS failed, retry - } - - // Case 2: Resize marker - if (head.isResizeMarker()) { - currentTable = helpResizeOrGetNextTable(currentTable, head); - continue table_loop; - } - - // Case 3: Lock head - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, index); - if (currentHead != head || head.isResizeMarker()) { - continue table_loop; // Retry - } - - TableEntry prev = null; - TableEntry node = head; - while (node != null) { - if (node.key == key) { - // Key found. Merge. - V oldValue = node.getValuePlain(); // Plain read in lock - V computedValue; - if (oldValue != null) { - try { - computedValue = function.apply(oldValue, value); // Apply function - } catch (Throwable t) { - ThrowUtil.throwUnchecked(t); - return null; - } - } else { - computedValue = value; // Use provided value if old was placeholder - } - - if (computedValue != null) { - node.setValueVolatile(computedValue); // Update (volatile write) - finalValue = computedValue; - sizeDelta = (oldValue == null) ? 1 : 0; // Size change if old was placeholder - } else { - // Remove mapping - finalValue = null; - sizeDelta = (oldValue != null) ? -1 : 0; // Size change if old was value - TableEntry next = node.getNextPlain(); // Plain read - if (prev == null) setAtIndexRelease(currentTable, index, next); - else prev.setNextRelease(next); - } - break table_loop; // Done - } - prev = node; - node = node.getNextPlain(); // Plain read - } // End while - - // Key not found. Add provided value. - finalValue = value; - sizeDelta = 1; - TableEntry newNode = new TableEntry<>(key, value); - if (prev != null) prev.setNextRelease(newNode); // Release write - else continue table_loop; // Should not happen - break table_loop; // Done - } // End synchronized(head) - } // End table_loop - - if (sizeDelta > 0) this.addSize(sizeDelta); - else if (sizeDelta < 0) this.subSize(-sizeDelta); - - return finalValue; - } - - - /** - * Removes all of the mappings from this map. - * The map will be empty after this call returns. - */ - public void clear() { - long removedCount = 0L; - TableEntry[] currentTable = this.table; // Volatile read - - for (int i = 0; i < currentTable.length; ++i) { - TableEntry head = getAtIndexVolatile(currentTable, i); - - if (head == null || head.isResizeMarker()) continue; - - // Lock bin to clear - synchronized (head) { - TableEntry currentHead = getAtIndexVolatile(currentTable, i); - // Re-check after lock - if (currentHead != head || head.isResizeMarker()) { - continue; // Bin changed, skip - } - - // Count actual mappings and clear bin - TableEntry node = head; - while (node != null) { - if (node.getValuePlain() != null) { // Count non-placeholders - removedCount++; - } - node = node.getNextPlain(); // Plain read in lock - } - // Clear bin head with release semantics - setAtIndexRelease(currentTable, i, null); - } // End synchronized - } // End loop - - if (removedCount > 0) { - this.subSize(removedCount); - } - } - - // --- Iterators and Views --- - - /** - * Returns an iterator over the map entries. - */ - public Iterator> entryIterator() { - return new EntryIterator<>(this); - } - - /** - * Returns an iterator over the map entries (implements Iterable). - */ - @Override - public final Iterator> iterator() { - return this.entryIterator(); - } - - /** - * Returns an iterator over the keys. - */ - public PrimitiveIterator.OfLong keyIterator() { - return new KeyIterator<>(this); - } - - /** - * Returns an iterator over the values. - */ - public Iterator valueIterator() { - return new ValueIterator<>(this); - } - - /** - * Returns a {@link Collection} view of the values contained in this map. - */ - public Collection values() { - Values v = this.values; - return (v != null) ? v : (this.values = new Values<>(this)); - } - - /** - * Returns a {@link Set} view of the mappings contained in this map. - */ - public Set> entrySet() { - EntrySet es = this.entrySet; - return (es != null) ? es : (this.entrySet = new EntrySet<>(this)); - } - - // --- Inner Classes: TableEntry, Iterators, Views --- - - /** - * Represents a key-value mapping entry in the hash table. - * Also used as a resize marker. - */ - public static final class TableEntry { - static final VarHandle TABLE_ENTRY_ARRAY_HANDLE; - private static final VarHandle VALUE_HANDLE; - private static final VarHandle NEXT_HANDLE; - - static { - try { - TABLE_ENTRY_ARRAY_HANDLE = ConcurrentUtil.getArrayHandle(TableEntry[].class); - VALUE_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "value", Object.class); - NEXT_HANDLE = ConcurrentUtil.getVarHandle(TableEntry.class, "next", TableEntry.class); - } catch (Throwable t) { - throw new Error("Failed to initialize TableEntry VarHandles", t); - } - } - - final long key; - private volatile V value; - private volatile TableEntry next; - private final boolean resizeMarker; - - /** - * Constructor for regular map entries. - */ - TableEntry(final long key, final V value) { - this(key, value, false); - } - - /** - * Constructor for potentially creating resize markers. - */ - TableEntry(final long key, final V value, final boolean resize) { - this.key = key; - this.resizeMarker = resize; - this.setValuePlain(value); // Initial plain set - } - - public long getKey() { - return this.key; - } - - public V getValue() { - return getValueVolatile(); - } - - public V setValue(V newValue) { - throw new UnsupportedOperationException("Direct setValue on TableEntry is not supported; use map methods."); - } - - @SuppressWarnings("unchecked") - V getValuePlain() { - return (V) VALUE_HANDLE.get(this); - } - - @SuppressWarnings("unchecked") - V getValueAcquire() { - return (V) VALUE_HANDLE.getAcquire(this); - } - - @SuppressWarnings("unchecked") - V getValueVolatile() { - return (V) VALUE_HANDLE.getVolatile(this); - } - - void setValuePlain(final V value) { - VALUE_HANDLE.set(this, value); - } - - void setValueRelease(final V value) { - VALUE_HANDLE.setRelease(this, value); - } - - void setValueVolatile(final V value) { - VALUE_HANDLE.setVolatile(this, value); - } - - boolean compareAndSetValueVolatile(final V expect, final V update) { - return VALUE_HANDLE.compareAndSet(this, expect, update); - } - - @SuppressWarnings("unchecked") - TableEntry getNextPlain() { - return (TableEntry) NEXT_HANDLE.get(this); - } - - @SuppressWarnings("unchecked") - TableEntry getNextVolatile() { - return (TableEntry) NEXT_HANDLE.getVolatile(this); - } - - void setNextPlain(final TableEntry next) { - NEXT_HANDLE.set(this, next); - } - - void setNextRelease(final TableEntry next) { - NEXT_HANDLE.setRelease(this, next); - } - - void setNextVolatile(final TableEntry next) { - NEXT_HANDLE.setVolatile(this, next); - } - - boolean isResizeMarker() { - return this.resizeMarker; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TableEntry that)) return false; - return key == that.key && Objects.equals(getValueVolatile(), that.getValueVolatile()); // Use volatile read for value - } - - @Override - public int hashCode() { - return Long.hashCode(key) ^ Objects.hashCode(getValueVolatile()); // Use volatile read for value - } - - @Override - public String toString() { - return key + "=" + getValueVolatile(); // Use volatile read for value - } - } - - /** - * Base class for traversing nodes, handling resizes. - * Note: This iterator implementation is simplified and might not be fully robust against - * rapid concurrent modifications during iteration, particularly multiple resize events. - * It aims for basic correctness in common scenarios. - */ - protected static class NodeIterator { - final LeafConcurrentLong2ReferenceChainedHashTable map; - TableEntry[] currentTable; - TableEntry nextNode; - int nextTableIndex; - TableEntry currentNodeInChain; // Current node within the chain being processed - - NodeIterator(TableEntry[] initialTable, LeafConcurrentLong2ReferenceChainedHashTable map) { - this.map = map; - this.currentTable = initialTable; // Start with the table state at iterator creation - this.nextNode = null; - // Start iteration from the end of the table backwards - this.nextTableIndex = (initialTable == null || initialTable.length == 0) ? -1 : initialTable.length - 1; - this.currentNodeInChain = null; - advance(); // Find the first element - } - - /** - * Advances to find the next valid node (non-null value, non-marker). - * Sets {@code nextNode}. Handles basic traversal and checks for table changes. - */ - final void advance() { - nextNode = null; // Assume no next node initially - - if (currentNodeInChain != null) { - currentNodeInChain = currentNodeInChain.getNextVolatile(); // Move to next in chain - } - - while (nextNode == null) { - if (currentNodeInChain != null) { - // Check if the node is valid (not marker, has value) - if (!currentNodeInChain.isResizeMarker() && currentNodeInChain.getValueVolatile() != null) { - nextNode = currentNodeInChain; // Found a valid node - return; // Exit advance - } - // Node invalid (marker or placeholder), move to the next - currentNodeInChain = currentNodeInChain.getNextVolatile(); - continue; // Check next node in chain - } - - if (nextTableIndex < 0) { - // Check if the underlying table reference changed (indicates resize) - // This is a simplified check; robust iterators might need more complex resize handling - if (this.currentTable != map.table) { - // Table changed, restart iteration from the new table - this.currentTable = map.table; - this.nextTableIndex = (this.currentTable == null || this.currentTable.length == 0) ? -1 : this.currentTable.length - 1; - this.currentNodeInChain = null; - // Retry finding a node from the beginning of the new table - continue; - } - // No table change and all bins checked - return; // Exhausted - } - - if (this.currentTable != null && this.nextTableIndex < this.currentTable.length) { - TableEntry head = getAtIndexVolatile(this.currentTable, this.nextTableIndex--); // Read head and decrement index - - if (head != null && !head.isResizeMarker()) { - // Start traversing this new chain - currentNodeInChain = head; - // Check if the head itself is a valid node - if (currentNodeInChain.getValueVolatile() != null) { - nextNode = currentNodeInChain; - return; // Found valid node (head of bin) - } - // Head is placeholder, continue loop to check next in chain - continue; - } - // Bin was empty or head was marker. Reset chain traversal. - currentNodeInChain = null; - } else { - // Table became null or index out of bounds (shouldn't happen unless table shrinks drastically) - // Force moving to next index to avoid infinite loop - nextTableIndex--; - currentNodeInChain = null; - // Consider checking map.table again here for robustness - if (this.currentTable != map.table) { - // Restart if table changed - this.currentTable = map.table; - this.nextTableIndex = (this.currentTable == null || this.currentTable.length == 0) ? -1 : this.currentTable.length - 1; - continue; - } - } - } // End while (nextNode == null) - } - - - public final boolean hasNext() { - return this.nextNode != null; - } - - /** - * Internal method to get the next node and advance. - */ - final TableEntry findNext() { - TableEntry e = this.nextNode; - if (e == null) { - return null; // Signifies end for internal use - } - advance(); // Prepare for the *next* call - return e; // Return the previously found node - } - } - - /** - * Base class for concrete iterators (Entry, Key, Value). - * Handles remove() and NoSuchElementException. - */ - protected static abstract class BaseIteratorImpl extends NodeIterator implements Iterator { - protected TableEntry lastReturned; // Node returned by last next() call - - protected BaseIteratorImpl(final LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map.table, map); // Initialize NodeIterator - this.lastReturned = null; - } - - /** - * Gets the next node, updates lastReturned, advances iterator. - */ - protected final TableEntry nextNode() throws NoSuchElementException { - TableEntry node = this.nextNode; // Node pre-fetched by advance() - if (node == null) { - throw new NoSuchElementException(); - } - this.lastReturned = node; // Store for remove() - advance(); // Find the *next* node for the subsequent call - return node; // Return the current node - } - - @Override - public void remove() { - TableEntry last = this.lastReturned; - if (last == null) { - throw new IllegalStateException("next() not called or remove() already called"); - } - this.map.remove(last.key); // Delegate removal to map's method - this.lastReturned = null; // Prevent double remove - } - - @Override - public abstract T next() throws NoSuchElementException; // Must be implemented by subclass - - @Override - public void forEachRemaining(final Consumer action) { - Validate.notNull(action, "Action may not be null"); - while (hasNext()) { - action.accept(next()); - } - } - } - - /** - * Iterator over map entries (TableEntry objects). - */ - protected static final class EntryIterator extends BaseIteratorImpl> { - EntryIterator(final LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map); - } - - @Override - public TableEntry next() throws NoSuchElementException { - return nextNode(); - } - } - - /** - * Iterator over map keys (long primitives). - */ - protected static final class KeyIterator extends BaseIteratorImpl implements PrimitiveIterator.OfLong { - KeyIterator(final LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map); - } - - @Override - public long nextLong() throws NoSuchElementException { - return nextNode().key; - } - - @Override - public Long next() throws NoSuchElementException { - return nextLong(); // Autoboxing - } - - @Override - public void forEachRemaining(final LongConsumer action) { - Validate.notNull(action, "Action may not be null"); - while (hasNext()) { - action.accept(nextLong()); - } - } - - @Override - public void forEachRemaining(final Consumer action) { - if (action instanceof LongConsumer) { - forEachRemaining((LongConsumer) action); - } else { - Validate.notNull(action, "Action may not be null"); - while (hasNext()) { - action.accept(nextLong()); // Autoboxing - } - } - } - } - - /** - * Iterator over map values. - */ - protected static final class ValueIterator extends BaseIteratorImpl { - ValueIterator(final LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map); - } - - @Override - public V next() throws NoSuchElementException { - return nextNode().getValueVolatile(); // Volatile read for value - } - } - - // --- Collection Views --- - - /** - * Base class for Collection views (Values, EntrySet). - */ - protected static abstract class BaseCollection implements Collection { - protected final LeafConcurrentLong2ReferenceChainedHashTable map; - - protected BaseCollection(LeafConcurrentLong2ReferenceChainedHashTable map) { - this.map = Validate.notNull(map); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean isEmpty() { - return map.isEmpty(); - } - - @Override - public abstract boolean contains(Object o); // Subclass responsibility - - @Override - public boolean containsAll(Collection c) { - Validate.notNull(c); - for (Object e : c) { - if (!contains(e)) return false; - } - return true; - } - - @Override - public Object[] toArray() { - List list = new ArrayList<>(map.size()); - for (E e : this) list.add(e); // Uses iterator() from subclass - return list.toArray(); - } - - @Override - public T[] toArray(T[] a) { - Validate.notNull(a); - List list = new ArrayList<>(map.size()); - for (E e : this) list.add(e); - return list.toArray(a); - } - - @Override - public void clear() { - map.clear(); - } - - @Override - public boolean add(E e) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(Object o) { - Iterator it = iterator(); // Subclass provides iterator - while (it.hasNext()) { - if (Objects.equals(o, it.next())) { - it.remove(); // Use iterator's safe remove - return true; - } - } - return false; - } - - @Override - public boolean removeAll(Collection c) { - Validate.notNull(c); - boolean modified = false; - Iterator it = iterator(); - while (it.hasNext()) { - if (c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - @Override - public boolean retainAll(Collection c) { - Validate.notNull(c); - boolean modified = false; - Iterator it = iterator(); - while (it.hasNext()) { - if (!c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - @Override - public boolean removeIf(Predicate filter) { - Validate.notNull(filter); - boolean removed = false; - Iterator it = iterator(); - while (it.hasNext()) { - if (filter.test(it.next())) { - it.remove(); - removed = true; - } - } - return removed; - } - - @Override - public String toString() { - Iterator it = iterator(); - if (!it.hasNext()) return "[]"; - StringBuilder sb = new StringBuilder("["); - for (;;) { - E e = it.next(); - sb.append(e == this ? "(this Collection)" : e); - if (!it.hasNext()) return sb.append(']').toString(); - sb.append(',').append(' '); - } - } - - @Override - public void forEach(Consumer action) { - Validate.notNull(action); - for (E e : this) { // Uses iterator() from subclass - action.accept(e); - } - } - } - - /** - * Collection view for the map's values. - */ - protected static final class Values extends BaseCollection { - Values(LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map); - } - - @Override - public boolean contains(Object o) { - try { - return o != null && map.containsValue((V) o); - } catch (ClassCastException cce) { - return false; - } - } - - @Override - public Iterator iterator() { - return map.valueIterator(); - } - } - - /** - * Set view for the map's entries (TableEntry objects). - */ - protected static final class EntrySet extends BaseCollection> implements Set> { - EntrySet(LeafConcurrentLong2ReferenceChainedHashTable map) { - super(map); - } - - @Override - public boolean contains(Object o) { - if (!(o instanceof LeafConcurrentLong2ReferenceChainedHashTable.TableEntry entry)) return false; - V mappedValue = map.get(entry.getKey()); // Concurrent read - // Use volatile read on entry's value for consistent comparison - return mappedValue != null && Objects.equals(mappedValue, entry.getValueVolatile()); - } - - @Override - public Iterator> iterator() { - return map.entryIterator(); - } - - @Override - public boolean remove(Object o) { - if (!(o instanceof LeafConcurrentLong2ReferenceChainedHashTable.TableEntry entry)) return false; - try { - // Use map's atomic remove(key, value) - // Use volatile read for the expected value - return map.remove(entry.getKey(), (V) entry.getValueVolatile()); - } catch (ClassCastException | NullPointerException cce) { // Handle potential type/null issues - return false; - } - } - - @Override - public int hashCode() { - int h = 0; - for (TableEntry e : this) { - h += e.hashCode(); // Uses entry's hashCode - } - return h; - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof Set c)) return false; - if (c.size() != size()) return false; - try { - // relies on containsAll checking entry equality correctly - return containsAll(c); - } catch (ClassCastException | NullPointerException unused) { - return false; - } - } - } -} From 8ee5743e433523e68b9ce4167835c088eafd2ab4 Mon Sep 17 00:00:00 2001 From: adabugra <57899270+adabugra@users.noreply.github.com> Date: Tue, 20 May 2025 00:32:30 +0300 Subject: [PATCH 67/81] Integrate Download API into CI workflow (#328) * Implement Build API integration to 1.21.4 build workflow * Rename cd dir * Enhance build workflow to dynamically fetch/clone repository * Move to publish-api.yml --- .github/workflows/publish-api.yml | 46 ++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 71bf488a..31cf9472 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -42,7 +42,7 @@ jobs: --no-daemon - name: Build - run: ./gradlew build -x test + run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon - name: Publish API continue-on-error: true @@ -52,3 +52,47 @@ jobs: env: REPO_USER: ${{ secrets.REPO_USER }} REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }} + + - name: Calculate SHA-256 + id: hash + run: | + FILE_NAME="leaf-1.21.4-${{ env.BUILD_NUMBER }}-mojmap.jar" + HASH=$(sha256sum "$FILE_NAME" | awk '{ print $1 }') + echo "sha256=$HASH" >> $GITHUB_OUTPUT + + - name: Rename JAR + run: | + mv "leaf-1.21.4-${{ env.BUILD_NUMBER }}-mojmap.jar" "leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" + + - name: Upload JAR to download API + uses: appleboy/scp-action@master + with: + host: ${{ secrets.API_HOST }} + username: ${{ secrets.API_USER }} + password: ${{ secrets.API_PASS }} + source: "./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" + target: "~/api/uploads/" + + - name: Insert build to download API + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.API_HOST }} + username: ${{ secrets.API_USER }} + password: ${{ secrets.API_PASS }} + script: | + BRANCH="${{ github.ref_name }}" + REPO_DIR="/root/Leaf-${BRANCH//\//-}" # Replace slashes with dashes to avoid directory issues + + [ -d "$REPO_DIR/.git" ] && cd "$REPO_DIR" && git fetch origin && git checkout "$BRANCH" && git reset --hard "origin/$BRANCH" || git clone --branch "$BRANCH" --depth 1 https://github.com/Winds-Studio/Leaf "$REPO_DIR" + + # Proceed to insert the build into the API + cd ~/api/cli + node insertBuild.js \ + --projectName leaf \ + --projectFriendlyName "Leaf" \ + --version 1.21.4 \ + --build-number ${{ env.BUILD_NUMBER }} \ + --repositoryPath "$REPO_DIR" \ + --storagePath /root/api/storage \ + --download "primary:/root/api/uploads/leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar:${{ steps.hash.outputs.sha256 }}" \ + --buildChannel default From b31bd407f818baa18704530dac14f2003df6225e Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 20 May 2025 07:21:32 +0800 Subject: [PATCH 68/81] Fix workflow * Revert * Cleanup --- .github/workflows/build-1214.yml | 62 +++++++++++++++++++++++++------ .github/workflows/build-pr.yml | 4 +- .github/workflows/publish-api.yml | 49 ++---------------------- scripts/prepareRelease.sh | 2 +- 4 files changed, 56 insertions(+), 61 deletions(-) diff --git a/.github/workflows/build-1214.yml b/.github/workflows/build-1214.yml index e19260f2..574e7d81 100644 --- a/.github/workflows/build-1214.yml +++ b/.github/workflows/build-1214.yml @@ -1,4 +1,5 @@ name: Build Leaf 1.21.4 + on: push: branches: [ "ver/1.21.4" ] @@ -10,8 +11,6 @@ jobs: env: BUILD_NUMBER: ${{ github.run_number }} GRADLE_MEMORY: "-Xmx4g -XX:MaxMetaspaceSize=2g" - outputs: - build_number: ${{ env.BUILD_NUMBER }} steps: - name: Checkout repository uses: actions/checkout@main @@ -82,16 +81,6 @@ jobs: - name: Create MojmapPaperclipJar run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon - - name: Rename Paperclip JARs - run: | - mv leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.4-${{ env.BUILD_NUMBER }}-mojmap.jar - - - name: Upload Leaf as build artifact - uses: actions/upload-artifact@main - with: - name: Leaf 1.21.4 - path: ./leaf-1.21.4-*.jar - - name: Prepare release notes and artifacts run: | chmod +x ./scripts/prepareRelease.sh @@ -101,6 +90,12 @@ jobs: GITHUB_REPO: ${{ github.repository }} BUILD_NUMBER: ${{ env.BUILD_NUMBER }} + - name: Upload Leaf + uses: actions/upload-artifact@main + with: + name: Leaf 1.21.4 + path: ./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar + - name: Release Leaf uses: softprops/action-gh-release@master with: @@ -112,3 +107,46 @@ jobs: target_commitish: "${{ github.sha }}" draft: false prerelease: false + + - name: Rename JAR + run: mv ./leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar + + - name: Calculate SHA-256 + id: hash + run: | + FILE_NAME="leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" + HASH=$(sha256sum "$FILE_NAME" | awk '{ print $1 }') + echo "sha256=$HASH" >> $GITHUB_OUTPUT + + - name: Upload JAR to download API + uses: appleboy/scp-action@master + with: + host: ${{ secrets.API_HOST }} + username: ${{ secrets.API_USER }} + password: ${{ secrets.API_PASS }} + source: "./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" + target: "~/api/uploads/" + + - name: Insert build to download API + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.API_HOST }} + username: ${{ secrets.API_USER }} + password: ${{ secrets.API_PASS }} + script: | + BRANCH="${{ github.ref_name }}" + REPO_DIR="/root/Leaf-${BRANCH//\//-}" # Replace slashes with dashes to avoid directory issues + + [ -d "$REPO_DIR/.git" ] && cd "$REPO_DIR" && git fetch origin && git checkout "$BRANCH" && git reset --hard "origin/$BRANCH" || git clone --branch "$BRANCH" --depth 1 https://github.com/Winds-Studio/Leaf "$REPO_DIR" + + # Proceed to insert the build into the API + cd ~/api/cli + node insertBuild.js \ + --projectName leaf \ + --projectFriendlyName "Leaf" \ + --version 1.21.4 \ + --build-number ${{ env.BUILD_NUMBER }} \ + --repositoryPath "$REPO_DIR" \ + --storagePath /root/api/storage \ + --download "primary:/root/api/uploads/leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar:${{ steps.hash.outputs.sha256 }}" \ + --buildChannel default diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index fd66d8b9..06a5d05f 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -34,9 +34,9 @@ jobs: - name: Rename Paperclip JARs run: | - mv leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.4-mojmap.jar + mv leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.4.jar - name: Upload Leaf as build artifact uses: actions/upload-artifact@main with: name: Leaf 1.21.4 - path: ./leaf-1.21.4-*.jar + path: ./leaf-1.21.4.jar diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml index 31cf9472..ea6c0561 100644 --- a/.github/workflows/publish-api.yml +++ b/.github/workflows/publish-api.yml @@ -1,4 +1,5 @@ name: Publish API + on: push: branches: [ "ver/1.21.4" ] @@ -41,10 +42,10 @@ jobs: --build-cache \ --no-daemon - - name: Build + - name: Create MojmapPaperclipJar run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon - - name: Publish API + - name: Publish Maven API continue-on-error: true run: | ./gradlew :leaf-api:publish @@ -52,47 +53,3 @@ jobs: env: REPO_USER: ${{ secrets.REPO_USER }} REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }} - - - name: Calculate SHA-256 - id: hash - run: | - FILE_NAME="leaf-1.21.4-${{ env.BUILD_NUMBER }}-mojmap.jar" - HASH=$(sha256sum "$FILE_NAME" | awk '{ print $1 }') - echo "sha256=$HASH" >> $GITHUB_OUTPUT - - - name: Rename JAR - run: | - mv "leaf-1.21.4-${{ env.BUILD_NUMBER }}-mojmap.jar" "leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" - - - name: Upload JAR to download API - uses: appleboy/scp-action@master - with: - host: ${{ secrets.API_HOST }} - username: ${{ secrets.API_USER }} - password: ${{ secrets.API_PASS }} - source: "./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar" - target: "~/api/uploads/" - - - name: Insert build to download API - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.API_HOST }} - username: ${{ secrets.API_USER }} - password: ${{ secrets.API_PASS }} - script: | - BRANCH="${{ github.ref_name }}" - REPO_DIR="/root/Leaf-${BRANCH//\//-}" # Replace slashes with dashes to avoid directory issues - - [ -d "$REPO_DIR/.git" ] && cd "$REPO_DIR" && git fetch origin && git checkout "$BRANCH" && git reset --hard "origin/$BRANCH" || git clone --branch "$BRANCH" --depth 1 https://github.com/Winds-Studio/Leaf "$REPO_DIR" - - # Proceed to insert the build into the API - cd ~/api/cli - node insertBuild.js \ - --projectName leaf \ - --projectFriendlyName "Leaf" \ - --version 1.21.4 \ - --build-number ${{ env.BUILD_NUMBER }} \ - --repositoryPath "$REPO_DIR" \ - --storagePath /root/api/storage \ - --download "primary:/root/api/uploads/leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar:${{ steps.hash.outputs.sha256 }}" \ - --buildChannel default diff --git a/scripts/prepareRelease.sh b/scripts/prepareRelease.sh index ef117aef..c55c5bae 100755 --- a/scripts/prepareRelease.sh +++ b/scripts/prepareRelease.sh @@ -10,7 +10,7 @@ CURRENT_TAG="ver-1.21.4" RELEASE_NOTES="release_notes.md" # Rename Leaf jar -mv ./$JAR_NAME-${BUILD_NUMBER}-mojmap.jar ./$JAR_NAME-${BUILD_NUMBER}.jar +mv ./leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./$JAR_NAME-${BUILD_NUMBER}.jar # Branch name CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) From a6b65f65dc36a606da1741eb9b637c3e21fefa48 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 20 May 2025 07:32:08 +0800 Subject: [PATCH 69/81] Fix workflow --- .github/workflows/build-1214.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-1214.yml b/.github/workflows/build-1214.yml index 574e7d81..e58299e1 100644 --- a/.github/workflows/build-1214.yml +++ b/.github/workflows/build-1214.yml @@ -108,9 +108,6 @@ jobs: draft: false prerelease: false - - name: Rename JAR - run: mv ./leaf-server/build/libs/leaf-paperclip-1.21.4-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.4-${{ env.BUILD_NUMBER }}.jar - - name: Calculate SHA-256 id: hash run: | From 7bedfe4e579de81c706ff7bb434cd9184b4fc08c Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 20 May 2025 08:35:35 +0800 Subject: [PATCH 70/81] Fix download API upload --- .github/workflows/build-1214.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-1214.yml b/.github/workflows/build-1214.yml index e58299e1..1289b100 100644 --- a/.github/workflows/build-1214.yml +++ b/.github/workflows/build-1214.yml @@ -142,6 +142,8 @@ jobs: --projectName leaf \ --projectFriendlyName "Leaf" \ --version 1.21.4 \ + --versionGroupName 1.21.4 \ + --versionName 1.21.4 \ --build-number ${{ env.BUILD_NUMBER }} \ --repositoryPath "$REPO_DIR" \ --storagePath /root/api/storage \ From 6e33899f1c5d75558c567a6273a36a70ba7ca20f Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Tue, 20 May 2025 22:01:10 +0900 Subject: [PATCH 71/81] cleanup --- .../util/map/AttributeInstanceArrayMap.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java index 13c865eb..b1fa9212 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java @@ -26,9 +26,7 @@ public class AttributeInstanceArrayMap implements Map, Attribu public AttributeInstanceArrayMap(final @NotNull Map, AttributeInstance> m) { this(); - for (AttributeInstance e : m.values()) { - setByIndex(e.getAttribute().value().uid, e); - } + putAll(m); } private void setByIndex(int index, @Nullable AttributeInstance instance) { @@ -99,7 +97,11 @@ public class AttributeInstanceArrayMap implements Map, Attribu @Override public final void putAll(@NotNull Map, ? extends AttributeInstance> m) { - m.forEach(this::put); + for (AttributeInstance e : m.values()) { + if (e != null) { + setByIndex(e.getAttribute().value().uid, e); + } + } } @Override @@ -134,14 +136,13 @@ public class AttributeInstanceArrayMap implements Map, Attribu @Override public final boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Map other)) return false; - return entrySet().equals(other.entrySet()); + if (!(o instanceof AttributeInstanceArrayMap that)) return false; + return size == that.size && Arrays.equals(a, that.a); } @Override public final int hashCode() { - return entrySet().hashCode(); + return Arrays.hashCode(a); } @Override From efc2a394796a6258d480c68cbdc89543a95e1f5a Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 21 May 2025 16:08:12 +0800 Subject: [PATCH 72/81] [ci skip] Cleanup --- build-data/leaf.at | 1 + .../0079-Hide-specified-item-components.patch | 4 +- .../features/0085-Multithreaded-Tracker.patch | 96 +++++++++---------- ...ntities-in-NearestLivingEntitySensor.patch | 5 +- ...-SparklyPaper-Parallel-world-ticking.patch | 18 ++-- ... => 0164-Reduce-PlayerChunk-Updates.patch} | 2 +- ... 0165-Async-switch-connection-state.patch} | 52 +++++----- ...ptimise-BlockEntities-tickersInLevel.patch | 4 +- ...he-cactus-can-even-survive-being-pla.patch | 17 +++- ...0168-Flush-location-while-knockback.patch} | 12 +-- .../0169-Only-tick-items-at-hand.patch | 28 +++--- ...mart-sort-items-in-NearestItemSensor.patch | 33 +++---- .../0172-Remove-streams-in-MobSensor.patch | 17 ++-- ...173-Remove-streams-in-TemptingSensor.patch | 31 ++---- ...se-HashedList-on-WeightedRandomList.patch} | 27 ++---- ...-death-item-drop-knockback-settings.patch} | 12 +-- ...patch => 0176-Optimize-AttributeMap.patch} | 26 ++--- ...-Optimize-getScaledTrackingDistance.patch} | 8 +- ...ptimize-SynchedEntityData-packDirty.patch} | 8 +- ...tion.patch => 0179-Paw-optimization.patch} | 6 +- .../features/0024-Multithreaded-Tracker.patch | 6 +- ...send.patch => 0036-Async-chunk-send.patch} | 2 +- ...0037-Optimise-player-movement-checks.patch | 6 +- .../leaf/async/chunk/AsyncChunkSend.java | 1 + .../async/chunk/AsyncChunkSendThread.java | 1 + .../async/tracker/MultithreadedTracker.java | 11 +-- .../modules/network/AlternativeJoin.java | 4 +- .../modules/opt/OptimizeItemTicking.java | 4 +- .../leaf/protocol/DoABarrelRollPackets.java | 1 + .../leaf/protocol/DoABarrelRollProtocol.java | 1 + .../leaf/protocol/LeafCustomPayload.java | 1 + .../org/dreeam/leaf/protocol/Protocol.java | 1 + .../org/dreeam/leaf/protocol/Protocols.java | 1 + .../leaf/util/list/HashedReferenceList.java | 4 +- .../util/map/AttributeInstanceArrayMap.java | 3 +- 35 files changed, 216 insertions(+), 238 deletions(-) rename leaf-server/minecraft-patches/features/{0164-reduce-PlayerChunk-Updates.patch => 0164-Reduce-PlayerChunk-Updates.patch} (98%) rename leaf-server/minecraft-patches/features/{0165-async-switch-connection-state.patch => 0165-Async-switch-connection-state.patch} (84%) rename leaf-server/minecraft-patches/features/{0168-flush-location-while-knockback.patch => 0168-Flush-location-while-knockback.patch} (84%) rename leaf-server/minecraft-patches/features/{0175-Use-HashedList-on-WeightedRandomList.patch => 0174-Use-HashedList-on-WeightedRandomList.patch} (56%) rename leaf-server/minecraft-patches/features/{0176-Add-configurable-death-item-drop-knockback-settings.patch => 0175-Add-configurable-death-item-drop-knockback-settings.patch} (80%) rename leaf-server/minecraft-patches/features/{0177-optimize-AttributeMap.patch => 0176-Optimize-AttributeMap.patch} (80%) rename leaf-server/minecraft-patches/features/{0178-optimize-getScaledTrackingDistance.patch => 0177-Optimize-getScaledTrackingDistance.patch} (76%) rename leaf-server/minecraft-patches/features/{0179-optimize-SynchedEntityData-packDirty.patch => 0178-Optimize-SynchedEntityData-packDirty.patch} (78%) rename leaf-server/minecraft-patches/features/{0174-paw-optimization.patch => 0179-Paw-optimization.patch} (98%) rename leaf-server/paper-patches/features/{0036-async-chunk-send.patch => 0036-Async-chunk-send.patch} (96%) diff --git a/build-data/leaf.at b/build-data/leaf.at index 227aec3d..b04d590a 100644 --- a/build-data/leaf.at +++ b/build-data/leaf.at @@ -4,6 +4,7 @@ protected net.minecraft.world.entity.Entity dimensions protected net.minecraft.world.entity.ai.goal.RemoveBlockGoal blockToRemove protected net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal getTargetConditions()Lnet/minecraft/world/entity/ai/targeting/TargetingConditions; protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape +public net.minecraft.server.level.ServerEntity sendDirtyEntityData()V public net.minecraft.util.Mth SIN public net.minecraft.world.entity.Entity updateInWaterStateAndDoWaterCurrentPushing()V public net.minecraft.world.entity.LivingEntity canGlide()Z diff --git a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch index 5745ed27..59eb1838 100644 --- a/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch +++ b/leaf-server/minecraft-patches/features/0079-Hide-specified-item-components.patch @@ -33,7 +33,7 @@ index c1130f596cf3443eeb62eb1b12587172fe0859ee..18590e0b1d94ee3266637c5f3ab65ead @Override diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..f4c4998a4913358e5164b44eb78b4f3df04778d2 100644 +index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..cee7daa4908efde754442bf7ef0932b94cf5ebca 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -306,7 +306,12 @@ public abstract class AbstractContainerMenu { @@ -44,7 +44,7 @@ index 3dcd8df0b395a8fed8bc0cbe0ff78f4ae0056fd3..f4c4998a4913358e5164b44eb78b4f3d + // Leaf start - Hide specified item components - Avoid some frequent client animations + final boolean matchResult = org.dreeam.leaf.config.modules.gameplay.HideItemComponent.enabled + ? !org.dreeam.leaf.util.item.ItemStackStripper.matchesStripped(this.getCarried(), this.remoteCarried) -+ : !ItemStack.matches(this.getCarried(), this.remoteCarried); // Paper - add flag to simplify remote matching logic ++ : !ItemStack.matches(this.getCarried(), this.remoteCarried); + if (matchResult) { + // Leaf end - Hide specified item components - Avoid some frequent client animations this.remoteCarried = this.getCarried().copy(); diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index 8fa0a7e1..a15a8293 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -37,7 +37,7 @@ index dd2509996bfd08e8c3f9f2be042229eac6d7692d..a35e9fae8f8da0c42f0616c4f78dc396 private static final byte CHUNK_TICKET_STAGE_NONE = 0; private static final byte CHUNK_TICKET_STAGE_LOADING = 1; diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542d50e7a98 100644 +index 5d9d233e3a568aa6297ed9c703fa450f98158602..24765ca23899b2eec049bf539c1f9eafc8b48d1b 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -248,6 +248,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -70,21 +70,16 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1073,12 +1089,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1073,7 +1089,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl -+ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl // Leaf - petal - Multithreaded tracker -+ public volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY; // Leaf - - // Paper start - optimise entity tracker - private long lastChunkUpdate = -1L; - private ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk lastTrackedChunk; - -+ public final Object sync = new Object(); // Leaf - Multithreaded tracker + // Leaf start - Multithreaded tracker + public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0]; ++ public final Object sync = new Object(); ++ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl ++ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY; + public ServerPlayerConnection[] seenBy() { + return seenByArray; + } @@ -92,11 +87,10 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 + this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY); + } + // Leaf end - Multithreaded tracker -+ - @Override - public final void moonrise$tick(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { - if (chunk == null) { -@@ -1100,27 +1128,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + + // Paper start - optimise entity tracker + private long lastChunkUpdate = -1L; +@@ -1100,27 +1127,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -111,21 +105,21 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { // need to purge any players possible not in the chunk list - for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ // Leaf start ++ // Leaf start - Multithreaded tracker + boolean removed = false; + for (final ServerPlayerConnection conn : this.seenBy()) { final ServerPlayer player = conn.getPlayer(); if (!players.contains(player)) { - this.removePlayer(player); + removed |= this.removePlayerMulti(player); -+ } -+ } + } + } + if (removed) { + this.seenByUpdated(); + } -+ // Leaf end -+ } -+ } + } + } ++ // Leaf end - Multithreaded tracker + + // Leaf start - Multithreaded tracker + public final @Nullable Runnable leafTickCompact(final ca.spottedleaf.moonrise.common.misc.NearbyPlayers.TrackedChunk chunk) { @@ -172,8 +166,8 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 + } + if (removed) { + this.seenByUpdated(); - } - } ++ } ++ } + }; + + // Only update asynchronously for real player, and sync update for fake players @@ -185,49 +179,49 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 + } else { + updatePlayerTasks.run(); + return null; - } - } ++ } ++ } + // Leaf end - Multithreaded tracker @Override public final void moonrise$removeNonTickThreadPlayers() { boolean foundToRemove = false; - for (final ServerPlayerConnection conn : this.seenBy) { -+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) { foundToRemove = true; break; -@@ -1131,12 +1227,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1131,12 +1226,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return; } - for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker ServerPlayer player = conn.getPlayer(); if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player)) { - this.removePlayer(player); -+ this.removePlayerMulti(player); // Leaf ++ this.removePlayerMulti(player); // Leaf - Multithreaded tracker } } -+ this.seenByUpdated(); // Leaf ++ this.seenByUpdated(); // Leaf - Multithreaded tracker } @Override -@@ -1146,10 +1243,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1146,10 +1242,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (this.seenBy.isEmpty()) { return; } - for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf ++ for (final ServerPlayerConnection conn : this.seenBy()) { // Leaf - Multithreaded tracker ServerPlayer player = conn.getPlayer(); - this.removePlayer(player); -+ this.removePlayerMulti(player); // Leaf ++ this.removePlayerMulti(player); // Leaf - Multithreaded tracker } -+ this.seenByUpdated(); // Leaf ++ this.seenByUpdated(); // Leaf - Multithreaded tracker } @Override -@@ -1176,7 +1274,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1176,7 +1273,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -236,7 +230,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 serverPlayerConnection.send(packet); } } -@@ -1189,21 +1287,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1189,21 +1286,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { @@ -246,7 +240,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 } } -+ // Leaf start ++ // Leaf start - Multithreaded tracker + public boolean removePlayerMulti(ServerPlayer player) { + if (this.seenBy.remove(player.connection)) { + this.serverEntity.removePairing(player); @@ -255,7 +249,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 + return false; + } + } -+ // Leaf end ++ // Leaf end - Multithreaded tracker + public void removePlayer(ServerPlayer player) { - org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot @@ -263,7 +257,7 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); } -+ this.seenByUpdated(); // Leaf ++ this.seenByUpdated(); // Leaf - Multithreaded tracker } public void updatePlayer(ServerPlayer player) { @@ -274,19 +268,19 @@ index 5d9d233e3a568aa6297ed9c703fa450f98158602..fa11526bf799910da9193ae6e3366542 // Paper start - remove allocation of Vec3D here // Vec3 vec3 = player.position().subtract(this.entity.position()); double vec3_dx = player.getX() - this.entity.getX(); -@@ -1231,6 +1342,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1231,6 +1341,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end if (flag) { if (this.seenBy.add(player.connection)) { -+ this.seenByUpdated(); // Leaf ++ this.seenByUpdated(); // Leaf - Multithreaded tracker // Paper start - entity tracking events if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) { this.serverEntity.addPairing(player); -@@ -1239,6 +1351,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1239,6 +1350,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker } } else if (this.seenBy.remove(player.connection)) { -+ this.seenByUpdated(); // Leaf ++ this.seenByUpdated(); // Leaf - Multithreaded tracker this.serverEntity.removePairing(player); } } @@ -304,7 +298,7 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6 public boolean visible = true; diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index d8298c7925e3bcea07ead4d438478cc51abcfa16..e67d87b3043b381d27f75f37e3b7f922e18dcc2d 100644 +index 1dee20436fc29537319ee456756a8e8f7b6fe66a..1f3e030cea42a0c9e27425cb18c232f482ef8608 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -70,6 +70,7 @@ public class ServerEntity { @@ -339,16 +333,14 @@ index d8298c7925e3bcea07ead4d438478cc51abcfa16..e67d87b3043b381d27f75f37e3b7f922 MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); if (savedData != null) { - for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper -+ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // Leaf ++ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers.toArray(ChunkMap.TrackedEntity.EMPTY_OBJECT_ARRAY)) { // Paper // Leaf - Multithreaded tracker final ServerPlayer serverPlayer = connection.getPlayer(); // Paper savedData.tickCarriedBy(serverPlayer, item); Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); -@@ -424,7 +433,13 @@ public class ServerEntity { - return Mth.unpackDegrees(this.lastSentYHeadRot); +@@ -425,6 +434,12 @@ public class ServerEntity { } -- private void sendDirtyEntityData() { -+ public void sendDirtyEntityData() { // Leaf - public + public void sendDirtyEntityData() { + // Leaf start - Multithreaded tracker + if (Thread.currentThread() instanceof org.dreeam.leaf.async.tracker.MultithreadedTracker.MultithreadedTrackerThread) { + wantSendDirtyEntityData = true; @@ -394,7 +386,7 @@ index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7e if (this.player.isRemoved()) { LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java -index c96f458994818392857642282ec3d492124885da..1caf6846c5057f2c9bcd56ba9f217cc2863cd41b 100644 +index c96f458994818392857642282ec3d492124885da..d345afd14ef6fe2f0a584df5dfa080fd7ab3f47e 100644 --- a/net/minecraft/world/entity/item/PrimedTnt.java +++ b/net/minecraft/world/entity/item/PrimedTnt.java @@ -142,12 +142,14 @@ public class PrimedTnt extends Entity implements TraceableEntity { @@ -404,7 +396,7 @@ index c96f458994818392857642282ec3d492124885da..1caf6846c5057f2c9bcd56ba9f217cc2 - ete.seenBy.stream() - .filter(viewer -> (viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16) - .forEach(viewer -> { -+ // Leaf start ++ // Leaf start - Multithreaded tracker + for (var viewer : ete.seenBy()) { + if ((viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16) { viewer.send(velocityPacket); @@ -412,7 +404,7 @@ index c96f458994818392857642282ec3d492124885da..1caf6846c5057f2c9bcd56ba9f217cc2 - }); + } + } -+ // Leaf end ++ // Leaf end - Multithreaded tracker } } // Paper end - Option to prevent TNT from moving in water diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 87c4d127..96454dc8 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,17 +12,16 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..d4d75a3f5d03533626417aaa3b0457ab98acea1c 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..ef87b95d53e5ea2555778e6020ea07a11c474961 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -13,17 +13,28 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; +@@ -13,17 +13,27 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; import net.minecraft.world.phys.AABB; public class NearestLivingEntitySensor extends Sensor { + + // Leaf start - Smart sort entities in NearestLivingEntitySensor + private final org.dreeam.leaf.util.FastBitRadixSort sorter; -+ + public NearestLivingEntitySensor() { + this.sorter = new org.dreeam.leaf.util.FastBitRadixSort(); + } diff --git a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch index 122f15f4..ff920aca 100644 --- a/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0134-SparklyPaper-Parallel-world-ticking.patch @@ -869,18 +869,10 @@ index 88b07fbb96b20124777889830afa480673629d43..f8bb32840129e57b7799f883cb4570d2 + // Leaf end - SparklyPaper - parallel world ticking mod (prevent clearing portal process) } diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java -index 3614551856c594f3c0cfee984fcf03fad672b007..f39e2e4b6a3b18a3fbfbd58c99560b9378b7a031 100644 +index 3614551856c594f3c0cfee984fcf03fad672b007..f4577f908ca9f279b72d89e5b0822d34b6fb7dd1 100644 --- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java +++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java -@@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.behavior; - import com.google.common.collect.ImmutableMap; - import java.util.Optional; - import net.minecraft.core.BlockPos; -+import net.minecraft.core.SectionPos; - import net.minecraft.core.GlobalPos; - import net.minecraft.network.protocol.game.DebugPackets; - import net.minecraft.server.level.ServerLevel; -@@ -44,14 +45,32 @@ public class GoToPotentialJobSite extends Behavior { +@@ -44,14 +44,34 @@ public class GoToPotentialJobSite extends Behavior { Optional memory = entity.getBrain().getMemory(MemoryModuleType.POTENTIAL_JOB_SITE); memory.ifPresent(globalPos -> { BlockPos blockPos = globalPos.pos(); @@ -890,6 +882,7 @@ index 3614551856c594f3c0cfee984fcf03fad672b007..f39e2e4b6a3b18a3fbfbd58c99560b93 - if (poiManager.exists(blockPos, holder -> true)) { - poiManager.release(blockPos); - } ++ // Leaf start - SparklyPaper - parallel world ticking + ServerLevel entityLevel = level; // Villager's current level + ServerLevel poiLevel = entityLevel.getServer().getLevel(globalPos.dimension()); // POI's actual level + @@ -909,7 +902,7 @@ index 3614551856c594f3c0cfee984fcf03fad672b007..f39e2e4b6a3b18a3fbfbd58c99560b93 - DebugPackets.sendPoiTicketCountPacket(level, blockPos); + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { // Added curly braces here + // Schedule POI operations on the POI's level thread, using POI's chunk coordinates for locality -+ poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(SectionPos.blockToSectionCoord(blockPos.getX()), SectionPos.blockToSectionCoord(blockPos.getZ()), poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); ++ poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(blockPos.getX() >> 4, blockPos.getZ() >> 4, poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + // Schedule debug packet on the entity's level thread, using entity's chunk coordinates for locality + entityLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(entity.chunkPosition().x, entity.chunkPosition().z, debugPacketTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + } @@ -917,6 +910,7 @@ index 3614551856c594f3c0cfee984fcf03fad672b007..f39e2e4b6a3b18a3fbfbd58c99560b93 + poiOperationsTask.run(); // This will use poiLevel's PoiManager but thread checks are permissive + debugPacketTask.run(); // This will use entityLevel's PoiManager + } ++ // Leaf end - SparklyPaper - parallel world ticking } }); entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE); @@ -1037,7 +1031,7 @@ index d212f57c8c0b2086f567fd30237b110203d9e8cb..ed4df82581b5411e54068ccc59ea85a7 } else { Entity entity = owner.teleport( diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index f4c4998a4913358e5164b44eb78b4f3df04778d2..cb0a064a41f4f72f4f1a8768b6a1961b903e7708 100644 +index cee7daa4908efde754442bf7ef0932b94cf5ebca..ff2ff95ec9d94e2e31e8174196b384c37d56f38a 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -92,8 +92,14 @@ public abstract class AbstractContainerMenu { diff --git a/leaf-server/minecraft-patches/features/0164-reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0164-reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch index ec87ac5d..8a4ce281 100644 --- a/leaf-server/minecraft-patches/features/0164-reduce-PlayerChunk-Updates.patch +++ b/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 10:08:54 +0200 -Subject: [PATCH] reduce PlayerChunk Updates +Subject: [PATCH] Reduce PlayerChunk Updates diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java diff --git a/leaf-server/minecraft-patches/features/0165-async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch similarity index 84% rename from leaf-server/minecraft-patches/features/0165-async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch index 03b4f3b5..d02cdc9f 100644 --- a/leaf-server/minecraft-patches/features/0165-async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0165-Async-switch-connection-state.patch @@ -1,22 +1,22 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 9 May 2025 16:55:34 +0900 -Subject: [PATCH] async switch connection state +Subject: [PATCH] Async switch connection state diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3a82e1e510029576485427af9fd705b37c5f6e20 100644 +index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f569350a53 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -342,6 +342,11 @@ public class Connection extends SimpleChannelInboundHandler> { if (protocolInfo.flow() != this.getReceiving()) { throw new IllegalStateException("Invalid inbound protocol: " + protocolInfo.id()); } else { -+ // Leaf start ++ // Leaf start - Async switch connection state + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { + this.channel.config().setAutoRead(false); + } -+ // Leaf end ++ // Leaf end - Async switch connection state this.packetListener = packetInfo; this.disconnectListener = null; UnconfiguredPipelineHandler.InboundConfigurationTask inboundConfigurationTask = UnconfiguredPipelineHandler.setupInboundProtocol(protocolInfo); @@ -25,34 +25,33 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3a82e1e510029576485427af9fd705b3 } - syncAfterConfigurationChange(this.channel.writeAndFlush(inboundConfigurationTask)); -+ // Leaf start ++ // Leaf start - Async switch connection state + var cf = this.channel.writeAndFlush(inboundConfigurationTask); + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { + cf.addListener((ChannelFutureListener) Connection::syncAfterConfigurationChange); + return; + } + syncAfterConfigurationChange(cf); -+ // Leaf end ++ // Leaf end - Async switch connection state } } -@@ -369,9 +381,41 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -369,7 +381,38 @@ public class Connection extends SimpleChannelInboundHandler> { } boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; - syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); + var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); -+ // Leaf start ++ // Leaf start - Async switch connection state + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { + if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { + throw new IllegalStateException("Thread failed netty thread check: Switching outbound protocol state use setupOutboundProtocolAsync instead"); + } + } -+ // Leaf end + syncAfterConfigurationChange(cf); + } + } -+ // Leaf start ++ + public @Nullable ChannelFuture setupOutboundProtocolAsync(ProtocolInfo protocolInfo) { + if (protocolInfo.flow() != this.getSending()) { + throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); @@ -73,24 +72,22 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3a82e1e510029576485427af9fd705b3 + return cf; + } + return null; ++ // Leaf end - Async switch connection state } } -+ // Leaf end - public void setListenerForServerboundHandshake(PacketListener packetListener) { - if (this.packetListener != null) { diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java -index 2e9eb04c7c4342393c05339906c267bca9ff29b1..c70d5a0db1dfd01eab323aefd07d6e81dd188927 100644 +index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe09ea7015a 100644 --- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java -@@ -140,11 +140,32 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis +@@ -140,11 +140,34 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis } } -+ private volatile boolean changingState = false; // Leaf ++ private volatile boolean changingState = false; // Leaf - Async switch connection state @Override public void handleConfigurationFinished(ServerboundFinishConfigurationPacket packet) { -+ // Leaf start ++ // Leaf start - Async switch connection state + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && !changingState) { + changingState = true; + this.finishCurrentTask(JoinWorldTask.TYPE); @@ -109,27 +106,29 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..c70d5a0db1dfd01eab323aefd07d6e81 + }); + return; + } -+ // Leaf end PacketUtils.ensureRunningOnSameThread(packet, this, this.server); - this.finishCurrentTask(JoinWorldTask.TYPE); - this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.finishCurrentTask(JoinWorldTask.TYPE); } // Leaf -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); } // Leaf ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { ++ this.finishCurrentTask(JoinWorldTask.TYPE); ++ this.connection.setupOutboundProtocol(GameProtocols.CLIENTBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()))); ++ } ++ // Leaf end - Async switch connection state try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 49f1743db193be1f10bfe6419231eb682e1068f7..b0ffd2077747b2325ab795eef457b9a0fa44754b 100644 +index 49f1743db193be1f10bfe6419231eb682e1068f7..8cbfe4abca6a3962376f723b94de86b4cab9d9e2 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -472,11 +472,32 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -472,11 +472,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } -+ private volatile boolean changingState = false; // Leaf ++ private volatile boolean changingState = false; // Leaf - Async switch connection state @Override public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { -+ // Leaf start ++ // Leaf start - Async switch connection state + if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled && !changingState) { + changingState = true; + this.connection.setupOutboundProtocolAsync(ConfigurationProtocols.CLIENTBOUND).addListener(l -> { @@ -147,12 +146,11 @@ index 49f1743db193be1f10bfe6419231eb682e1068f7..b0ffd2077747b2325ab795eef457b9a0 + }); + return; + } -+ // Leaf end -+ ++ // Leaf end - Async switch connection state PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); - this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); } // Leaf ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); // Leaf - Async switch connection state CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); ServerConfigurationPacketListenerImpl serverConfigurationPacketListenerImpl = new ServerConfigurationPacketListenerImpl( this.server, this.connection, commonListenerCookie, this.player // CraftBukkit diff --git a/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch index 0f09babb..1ad19b8d 100644 --- a/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch +++ b/leaf-server/minecraft-patches/features/0166-Optimise-BlockEntities-tickersInLevel.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise BlockEntities tickersInLevel diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 546fb78339c005ed71142cb3c894f816b8c72d08..22ccc6bc8444f2dba1db8a0e06d5018b6631c442 100644 +index 546fb78339c005ed71142cb3c894f816b8c72d08..e6eab6929b08503c49debbbd25497ffedad438e1 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -72,7 +72,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -13,7 +13,7 @@ index 546fb78339c005ed71142cb3c894f816b8c72d08..22ccc6bc8444f2dba1db8a0e06d5018b } }; - private final Map tickersInLevel = Maps.newHashMap(); -+ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimiseBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); ++ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimiseBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); // Leaf - Optimise BlockEntities tickersInLevel public boolean loaded; public final ServerLevel level; // CraftBukkit - type @Nullable diff --git a/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch index a985202f..b465fe70 100644 --- a/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch +++ b/leaf-server/minecraft-patches/features/0167-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch @@ -1,8 +1,21 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Thu, 8 May 2025 21:24:48 +0200 +From: Yive <6853318+Yive@users.noreply.github.com> +Date: Tue, 11 Jul 2023 13:27:01 -0700 Subject: [PATCH] Pluto: Check if the cactus can even survive being placed +Original license: GPLv3 +Original project: https://github.com/Yive/Pluto + +Results at 3,000 randomTickSpeed and 24,448 cacti: + +check-survival-before-growth - false & doTileDrop - true: 48mspt +check-survival-before-growth - true & doTileDrop - true: 25mspt +check-survival-before-growth - false & doTileDrop - false: 18mspt +check-survival-before-growth - true & doTileDrop - false: 6mspt + +Setting the gamerule "doTileDrop" to false was to simulate a server that has chunk collectors. + +Note: This might increase the item output of a cacti farm, though in theory it should act the same as vanilla. diff --git a/net/minecraft/world/level/block/CactusBlock.java b/net/minecraft/world/level/block/CactusBlock.java index 079b4c95cf81119ca99daeb159aefca389afed74..8fe29455d7ae44f43c663718d38ea2d8cf639797 100644 diff --git a/leaf-server/minecraft-patches/features/0168-flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0168-Flush-location-while-knockback.patch similarity index 84% rename from leaf-server/minecraft-patches/features/0168-flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0168-Flush-location-while-knockback.patch index 27a24946..79ccc1cc 100644 --- a/leaf-server/minecraft-patches/features/0168-flush-location-while-knockback.patch +++ b/leaf-server/minecraft-patches/features/0168-Flush-location-while-knockback.patch @@ -1,24 +1,24 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 11 May 2025 19:45:58 +0200 -Subject: [PATCH] flush location while knockback +Subject: [PATCH] Flush location while knockback diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 477455fdfcc591a89823e88983eb12dabb078d9b..67c4cbb8ed6131b639b01e4fc0cbf56057a3fe03 100644 +index 477455fdfcc591a89823e88983eb12dabb078d9b..8469cf9cacebea3fdd855f6c3e9e2cf61c78b8ac 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java @@ -1412,6 +1412,13 @@ public abstract class Player extends LivingEntity { } if (!cancelled) { -+ // Leaf start ++ // Leaf start - Flush location while knockback + if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && target instanceof ServerPlayer targetPlayer && this instanceof ServerPlayer player1) { + targetPlayer.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(this)); + player1.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(targetPlayer)); + player1.connection.send(net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket.of(targetPlayer)); + } -+ // Leaf end ++ // Leaf end - Flush location while knockback ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); target.hurtMarked = false; target.setDeltaMovement(deltaMovement); @@ -26,12 +26,12 @@ index 477455fdfcc591a89823e88983eb12dabb078d9b..67c4cbb8ed6131b639b01e4fc0cbf560 } this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value -+ // Leaf start ++ // Leaf start - Flush location while knockback + if (org.dreeam.leaf.config.modules.gameplay.Knockback.flushKnockback && this instanceof ServerPlayer player1 && target instanceof ServerPlayer target1) { + target1.connection.connection.flushChannel(); + player1.connection.connection.flushChannel(); + } -+ // Leaf end ++ // Leaf end - Flush location while knockback } else { this.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility // CraftBukkit start - resync on cancelled event diff --git a/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch index c2917c5d..0adbe382 100644 --- a/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0169-Only-tick-items-at-hand.patch @@ -5,41 +5,45 @@ Subject: [PATCH] Only tick items at hand diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 50cf63666071f5d01a85dfc6c6c45c19b05d8ec2..f5afaaa8db28295425ff3c6db73917210eadc3ca 100644 +index 50cf63666071f5d01a85dfc6c6c45c19b05d8ec2..ef53b9f307572dd5dc99d02e017d6b2dafb04e3f 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -888,9 +888,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -888,12 +888,19 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc super.tick(); } -- for (int i = 0; i < this.getInventory().getContainerSize(); i++) { -- ItemStack item = this.getInventory().getItem(i); -- if (!item.isEmpty()) { ++ // Leaf start - Only tick items at hand + if (org.dreeam.leaf.config.modules.opt.OptimizeItemTicking.onlyTickItemsInHand) { + this.synchronizeSpecialItemUpdates(this.getMainHandItem()); + this.synchronizeSpecialItemUpdates(this.getOffhandItem()); + } else { -+ for (int i = 0; i < this.getInventory().getContainerSize(); ++i) { -+ ItemStack item = this.getInventory().getItem(i); -+ if (item.isEmpty()) continue; + for (int i = 0; i < this.getInventory().getContainerSize(); i++) { + ItemStack item = this.getInventory().getItem(i); + if (!item.isEmpty()) { this.synchronizeSpecialItemUpdates(item); } } ++ } ++ // Leaf end - Only tick items at hand + + if (this.getHealth() != this.lastSentHealth + || this.lastSentFood != this.foodData.getFoodLevel() diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 67c4cbb8ed6131b639b01e4fc0cbf56057a3fe03..8bab487a1d77d8227bf9426d0218e906db034982 100644 +index 8469cf9cacebea3fdd855f6c3e9e2cf61c78b8ac..d39757fae68580abcb13804e01587e542f1e087d 100644 --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -631,7 +631,12 @@ public abstract class Player extends LivingEntity { +@@ -631,7 +631,14 @@ public abstract class Player extends LivingEntity { } this.tickRegeneration(); -- this.inventory.tick(); ++ // Leaf start - Only tick items at hand + if (org.dreeam.leaf.config.modules.opt.OptimizeItemTicking.onlyTickItemsInHand) { + this.getMainHandItem().inventoryTick(this.level(), this, 0, true); + this.getOffhandItem().inventoryTick(this.level(), this, 0, true); + } else { -+ this.inventory.tick(); + this.inventory.tick(); + } ++ // Leaf end - Only tick items at hand this.oBob = this.bob; if (this.abilities.flying && !this.isPassenger()) { this.resetFallDistance(); diff --git a/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch index 3f02bbde..0907f553 100644 --- a/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch +++ b/leaf-server/minecraft-patches/features/0170-Smart-sort-items-in-NearestItemSensor.patch @@ -5,41 +5,32 @@ Subject: [PATCH] Smart sort items in NearestItemSensor diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -index 09fd13e2d958da8326276c4dadf25bf488aff5ac..a6268791efba64aa21957b7883a5691b5a07cc1a 100644 +index 09fd13e2d958da8326276c4dadf25bf488aff5ac..651797720f7fc6ff9dc614f4de56053c304b1170 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -@@ -6,6 +6,7 @@ import java.util.List; - import java.util.Optional; - import java.util.Set; - import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.ai.Brain; - import net.minecraft.world.entity.ai.memory.MemoryModuleType; -@@ -16,6 +17,8 @@ public class NearestItemSensor extends Sensor { +@@ -16,6 +16,12 @@ public class NearestItemSensor extends Sensor { private static final long Y_RANGE = 16L; public static final int MAX_DISTANCE_TO_WANTED_ITEM = 32; ++ // Leaf start - Smart sort items in NearestItemSensor + private final org.dreeam.leaf.util.FastBitRadixSort itemSorter; -+ public NearestItemSensor() {this.itemSorter = new org.dreeam.leaf.util.FastBitRadixSort();} ++ public NearestItemSensor() { ++ this.itemSorter = new org.dreeam.leaf.util.FastBitRadixSort(); ++ } ++ // Leaf end - Smart sort items in NearestItemSensor @Override public Set> requires() { return ImmutableSet.of(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM); -@@ -24,12 +27,13 @@ public class NearestItemSensor extends Sensor { - @Override +@@ -25,10 +31,10 @@ public class NearestItemSensor extends Sensor { protected void doTick(ServerLevel level, Mob entity) { Brain brain = entity.getBrain(); -- List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities + List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); -+ List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); -+ ItemEntity[] sortedItems = this.itemSorter.sort(entitiesOfClass, entity, ItemEntity.class); -+ ++ ItemEntity[] sortedItems = this.itemSorter.sort(entitiesOfClass, entity, ItemEntity.class); // Leaf - Smart sort items in NearestItemSensor // Paper start - Perf: remove streams from hot code ItemEntity nearest = null; - for (final ItemEntity itemEntity : entitiesOfClass) { -- if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities -+ for (final ItemEntity itemEntity : sortedItems) { -+ if (entity.hasLineOfSight(itemEntity)) { ++ for (final ItemEntity itemEntity : sortedItems) { // Leaf - Smart sort items in NearestItemSensor + if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities nearest = itemEntity; break; - } diff --git a/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch index 40643cb2..3c2dcbd5 100644 --- a/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch +++ b/leaf-server/minecraft-patches/features/0172-Remove-streams-in-MobSensor.patch @@ -5,22 +5,25 @@ Subject: [PATCH] Remove streams in MobSensor diff --git a/net/minecraft/world/entity/ai/sensing/MobSensor.java b/net/minecraft/world/entity/ai/sensing/MobSensor.java -index bda210b4809a5aade7ab4d0f26fdda4d5f53f619..18c7fe0b39b366d3ac4fd83415953c063e5c562f 100644 +index bda210b4809a5aade7ab4d0f26fdda4d5f53f619..2271196768bfc90626e007a70602f818c832e348 100644 --- a/net/minecraft/world/entity/ai/sensing/MobSensor.java +++ b/net/minecraft/world/entity/ai/sensing/MobSensor.java -@@ -40,7 +40,14 @@ public class MobSensor extends Sensor { +@@ -40,10 +40,15 @@ public class MobSensor extends Sensor { public void checkForMobsNearby(T sensingEntity) { Optional> memory = sensingEntity.getBrain().getMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES); if (!memory.isEmpty()) { - boolean flag = memory.get().stream().anyMatch(livingEntity -> this.mobTest.test(sensingEntity, livingEntity)); -+ boolean flag = false; +- if (flag) { +- this.mobDetected(sensingEntity); ++ // Leaf start - Remove streams in MobSensor + List entities = memory.get(); + for (LivingEntity livingEntity : entities) { + if (this.mobTest.test(sensingEntity, livingEntity)) { -+ flag = true; ++ this.mobDetected(sensingEntity); + break; + } -+ } - if (flag) { - this.mobDetected(sensingEntity); } ++ // Leaf end - Remove streams in MobSensor + } + } + diff --git a/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch index f6ec76a7..22bd01c0 100644 --- a/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch +++ b/leaf-server/minecraft-patches/features/0173-Remove-streams-in-TemptingSensor.patch @@ -5,25 +5,10 @@ Subject: [PATCH] Remove streams in TemptingSensor diff --git a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java -index 4b3ba795bc18417f983600f1edbc1895ccb7deab..48fef89a7a3286c17a56a842fa44b80797971d2f 100644 +index 4b3ba795bc18417f983600f1edbc1895ccb7deab..16bec10543419b3010af414f1b6d12068c3a4ea7 100644 --- a/net/minecraft/world/entity/ai/sensing/TemptingSensor.java +++ b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java -@@ -1,12 +1,13 @@ - package net.minecraft.world.entity.ai.sensing; - - import com.google.common.collect.ImmutableSet; -+import java.util.ArrayList; - import java.util.Comparator; - import java.util.List; - import java.util.Set; - import java.util.function.Predicate; --import java.util.stream.Collectors; - import net.minecraft.server.level.ServerLevel; -+import net.minecraft.server.level.ServerPlayer; - import net.minecraft.world.entity.EntitySelector; - import net.minecraft.world.entity.PathfinderMob; - import net.minecraft.world.entity.ai.Brain; -@@ -36,14 +37,21 @@ public class TemptingSensor extends Sensor { +@@ -36,15 +36,21 @@ public class TemptingSensor extends Sensor { protected void doTick(ServerLevel level, PathfinderMob entity) { Brain brain = entity.getBrain(); TargetingConditions targetingConditions = TEMPT_TARGETING.copy().range((float)entity.getAttributeValue(Attributes.TEMPT_RANGE)); @@ -35,10 +20,9 @@ index 4b3ba795bc18417f983600f1edbc1895ccb7deab..48fef89a7a3286c17a56a842fa44b807 - .filter(serverPlayer -> !entity.hasPassenger(serverPlayer)) - .sorted(Comparator.comparingDouble(entity::distanceToSqr)) - .collect(Collectors.toList()); -+ -+ List allPlayers = level.players(); -+ List list = new ArrayList<>(); -+ ++ // Leaf start - Remove streams in TemptingSensor ++ List allPlayers = level.players(); ++ List list = new java.util.ArrayList<>(); + for (Player serverPlayer : allPlayers) { + if (EntitySelector.NO_SPECTATORS.test(serverPlayer) && + targetingConditions.test(level, entity, serverPlayer) && @@ -47,9 +31,10 @@ index 4b3ba795bc18417f983600f1edbc1895ccb7deab..48fef89a7a3286c17a56a842fa44b807 + list.add(serverPlayer); + } + } -+ -+ list.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ // Leaf end - Remove streams in TemptingSensor + if (!list.isEmpty()) { ++ list.sort(Comparator.comparingDouble(entity::distanceToSqr)); // Leaf - Remove streams in TemptingSensor Player player = list.get(0); // CraftBukkit start + EntityTargetLivingEntityEvent event = CraftEventFactory.callEntityTargetLivingEvent(entity, player, EntityTargetEvent.TargetReason.TEMPT); diff --git a/leaf-server/minecraft-patches/features/0175-Use-HashedList-on-WeightedRandomList.patch b/leaf-server/minecraft-patches/features/0174-Use-HashedList-on-WeightedRandomList.patch similarity index 56% rename from leaf-server/minecraft-patches/features/0175-Use-HashedList-on-WeightedRandomList.patch rename to leaf-server/minecraft-patches/features/0174-Use-HashedList-on-WeightedRandomList.patch index 4508b07c..9e32b823 100644 --- a/leaf-server/minecraft-patches/features/0175-Use-HashedList-on-WeightedRandomList.patch +++ b/leaf-server/minecraft-patches/features/0174-Use-HashedList-on-WeightedRandomList.patch @@ -5,39 +5,28 @@ Subject: [PATCH] Use HashedList on WeightedRandomList diff --git a/net/minecraft/util/random/WeightedRandomList.java b/net/minecraft/util/random/WeightedRandomList.java -index d81648b9159b765ea3ff99abd46c065ac7f08d6b..76678beea53dac2fa7c394e97a6616a32ac08a64 100644 +index d81648b9159b765ea3ff99abd46c065ac7f08d6b..dc141f55f2df22b49b7db034cad9a2f57f6b8731 100644 --- a/net/minecraft/util/random/WeightedRandomList.java +++ b/net/minecraft/util/random/WeightedRandomList.java -@@ -2,6 +2,8 @@ package net.minecraft.util.random; - - import com.google.common.collect.ImmutableList; - import com.mojang.serialization.Codec; -+ -+import java.util.Collections; - import java.util.List; - import java.util.Objects; - import java.util.Optional; -@@ -11,10 +13,12 @@ import net.minecraft.util.RandomSource; +@@ -11,10 +11,12 @@ import net.minecraft.util.RandomSource; public class WeightedRandomList { private final int totalWeight; private final ImmutableList items; -+ private List entryHashList; ++ private List entryHashList; // Leaf - Use HashedList on WeightedRandomList WeightedRandomList(List items) { this.items = ImmutableList.copyOf(items); this.totalWeight = WeightedRandom.getTotalWeight(items); -+ this.entryHashList = this.items.size() > 4 ? this.items : Collections.unmodifiableList(new org.dreeam.leaf.util.list.HashedReferenceList(this.items)); ++ this.entryHashList = this.items.size() > 4 ? this.items : java.util.Collections.unmodifiableList(new org.dreeam.leaf.util.list.HashedReferenceList<>(this.items)); // Leaf - Use HashedList on WeightedRandomList } public static WeightedRandomList create() { -@@ -43,9 +47,7 @@ public class WeightedRandomList { - } +@@ -44,7 +46,7 @@ public class WeightedRandomList { } -- public List unwrap() { + public List unwrap() { - return this.items; -- } -+ public List unwrap() {return this.entryHashList;} ++ return this.entryHashList; // Leaf - Use HashedList on WeightedRandomList + } public static Codec> codec(Codec elementCodec) { - return elementCodec.listOf().xmap(WeightedRandomList::create, WeightedRandomList::unwrap); diff --git a/leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch similarity index 80% rename from leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch rename to leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch index 4dfe585c..3e1c19dd 100644 --- a/leaf-server/minecraft-patches/features/0176-Add-configurable-death-item-drop-knockback-settings.patch +++ b/leaf-server/minecraft-patches/features/0175-Add-configurable-death-item-drop-knockback-settings.patch @@ -5,27 +5,27 @@ Subject: [PATCH] Add configurable death item drop knockback settings diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index f5afaaa8db28295425ff3c6db73917210eadc3ca..84d105c714c7cf2c015fb847740e25bb51f6d177 100644 +index 4275caa2b4d636f646f185d67e1cdbc194bd68aa..cacceac0ee28fc045e97c0d062729390b51c4794 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1096,7 +1096,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1099,7 +1099,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc if (!keepInventory) { for (ItemStack item : this.getInventory().getContents()) { if (!item.isEmpty() && !EnchantmentHelper.has(item, net.minecraft.world.item.enchantment.EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) { - loot.add(new DefaultDrop(item, stack -> this.drop(stack, true, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event) -+ loot.add(new DefaultDrop(item, stack -> this.drop(stack, org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.dropAround, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event) ++ loot.add(new DefaultDrop(item, stack -> this.drop(stack, org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.dropAround, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event) // Leaf - Add configurable death item drop knockback settings } } } -@@ -2849,9 +2849,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -2852,9 +2852,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } if (dropAround) { - float f = this.random.nextFloat() * 0.5F; -+ float f = this.random.nextFloat() * (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.horizontalForce; ++ float f = this.random.nextFloat() * (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.horizontalForce; // Leaf - Add configurable death item drop knockback settings float f1 = this.random.nextFloat() * (float) (Math.PI * 2); - itemEntity.setDeltaMovement(-Mth.sin(f1) * f, 0.2F, Mth.cos(f1) * f); -+ itemEntity.setDeltaMovement(-Mth.sin(f1) * f, (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.verticalForce, Mth.cos(f1) * f); ++ itemEntity.setDeltaMovement(-Mth.sin(f1) * f, (float) org.dreeam.leaf.config.modules.gameplay.DeathItemDropKnockback.verticalForce, Mth.cos(f1) * f); // Leaf - Add configurable death item drop knockback settings } else { float f = 0.3F; float f1 = Mth.sin(this.getXRot() * (float) (Math.PI / 180.0)); diff --git a/leaf-server/minecraft-patches/features/0177-optimize-AttributeMap.patch b/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch similarity index 80% rename from leaf-server/minecraft-patches/features/0177-optimize-AttributeMap.patch rename to leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch index 4c3257d5..da4562d9 100644 --- a/leaf-server/minecraft-patches/features/0177-optimize-AttributeMap.patch +++ b/leaf-server/minecraft-patches/features/0176-Optimize-AttributeMap.patch @@ -1,31 +1,31 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 15 May 2025 21:11:18 +0900 -Subject: [PATCH] optimize AttributeMap +Subject: [PATCH] Optimize AttributeMap diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java -index f8419dde44ebc7324e783f8bee42132d5ec973c3..1c52a118bad6b4b8abdf2527347ef66888b71f54 100644 +index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644 --- a/net/minecraft/world/entity/ai/attributes/Attribute.java +++ b/net/minecraft/world/entity/ai/attributes/Attribute.java @@ -16,10 +16,15 @@ public class Attribute { private boolean syncable; private final String descriptionId; private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE; -+ // Leaf start - optimize AttributeMap ++ // Leaf start - Optimize AttributeMap + public final int uid; + private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger(); -+ // Leaf end - optimize AttributeMap ++ // Leaf end - Optimize AttributeMap protected Attribute(String descriptionId, double defaultValue) { this.defaultValue = defaultValue; this.descriptionId = descriptionId; -+ this.uid = SIZE.getAndAdd(1); // Leaf - optimize AttributeMap ++ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap } public double getDefaultValue() { diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb9263944e 100644 +index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..51a5a3a804a1cbb0e1d23be432043552b102d837 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java @@ -20,12 +20,12 @@ import org.slf4j.Logger; @@ -33,13 +33,13 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb private static final Logger LOGGER = LogUtils.getLogger(); // Gale start - Lithium - replace AI attributes with optimized collections - private final Map, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); -+ private final Map, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - optimize AttributeMap ++ private final Map, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - Optimize AttributeMap private final Set attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); private final Set attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); // Gale end - Lithium - replace AI attributes with optimized collections private final AttributeSupplier supplier; - private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations -+ // private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations ++ //private final java.util.function.Function, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables public AttributeMap(AttributeSupplier supplier) { @@ -48,7 +48,7 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb // Purpur end - Ridables this.supplier = defaultAttributes; - this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations -+ // this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations ++ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap } private void onAttributeModified(AttributeInstance instance) { @@ -57,7 +57,7 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb @Nullable public AttributeInstance getInstance(Holder attribute) { - return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways -+ // Leaf start - optimize AttributeMap ++ // Leaf start - Optimize AttributeMap + AttributeInstance v; + if ((v = this.attributes.get(attribute)) == null) { + AttributeInstance newValue; @@ -67,12 +67,12 @@ index 89f4c5b2d61e27acd48063f9f24ce9ea91898b8b..19f7119256ac50ba8db961f179d3fabb + } + } + return v; -+ // Leaf end - optimize AttributeMap ++ // Leaf end - Optimize AttributeMap } public boolean hasAttribute(Holder attribute) { diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java -index 24710041ccbc70e5506d8d89ae34f0141977f209..608edf272735d356215cf0147e65dcef391e1638 100644 +index 24710041ccbc70e5506d8d89ae34f0141977f209..09341ef6c651150aba223689badbead490162b2b 100644 --- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java @@ -11,7 +11,7 @@ public class AttributeSupplier { @@ -80,7 +80,7 @@ index 24710041ccbc70e5506d8d89ae34f0141977f209..608edf272735d356215cf0147e65dcef AttributeSupplier(Map, AttributeInstance> instances) { - this.instances = instances; -+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - optimize AttributeMap ++ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap } public AttributeInstance getAttributeInstance(Holder attribute) { diff --git a/leaf-server/minecraft-patches/features/0178-optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0177-Optimize-getScaledTrackingDistance.patch similarity index 76% rename from leaf-server/minecraft-patches/features/0178-optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0177-Optimize-getScaledTrackingDistance.patch index be9b6476..7f8ac17e 100644 --- a/leaf-server/minecraft-patches/features/0178-optimize-getScaledTrackingDistance.patch +++ b/leaf-server/minecraft-patches/features/0177-Optimize-getScaledTrackingDistance.patch @@ -1,11 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 17 May 2025 19:01:50 +0900 -Subject: [PATCH] optimize getScaledTrackingDistance +Subject: [PATCH] Optimize getScaledTrackingDistance diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java -index eb0589b203bcf72cd24bb37f2c448c23cb8d6f2b..a1ec070b8620ba34bae9103f19307f42e6ed2a7c 100644 +index eb0589b203bcf72cd24bb37f2c448c23cb8d6f2b..a54f9030c81a2eb36f4dae951b09a9a6057be936 100644 --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java @@ -838,7 +838,13 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface @@ -13,13 +13,13 @@ index eb0589b203bcf72cd24bb37f2c448c23cb8d6f2b..a1ec070b8620ba34bae9103f19307f42 @Override public int getScaledTrackingDistance(int trackingDistance) { - return this.getProperties().entityBroadcastRangePercentage * trackingDistance / 100; -+ // Leaf start ++ // Leaf start - Optimize getScaledTrackingDistance + int p = this.getProperties().entityBroadcastRangePercentage; + if (p == 100) { + return trackingDistance; + } + return p * trackingDistance / 100; -+ // Leaf end ++ // Leaf end - Optimize getScaledTrackingDistance } @Override diff --git a/leaf-server/minecraft-patches/features/0179-optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0178-Optimize-SynchedEntityData-packDirty.patch similarity index 78% rename from leaf-server/minecraft-patches/features/0179-optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0178-Optimize-SynchedEntityData-packDirty.patch index bbfe075e..324a6646 100644 --- a/leaf-server/minecraft-patches/features/0179-optimize-SynchedEntityData-packDirty.patch +++ b/leaf-server/minecraft-patches/features/0178-Optimize-SynchedEntityData-packDirty.patch @@ -1,11 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 17 May 2025 19:03:31 +0900 -Subject: [PATCH] optimize SynchedEntityData#packDirty +Subject: [PATCH] Optimize SynchedEntityData#packDirty diff --git a/net/minecraft/network/syncher/SynchedEntityData.java b/net/minecraft/network/syncher/SynchedEntityData.java -index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..546c36d0bc14b8db49245ff162be3dbc4d680da6 100644 +index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..f0b8d44009ebfaf7a45f9cbaea5dc3ff157815fc 100644 --- a/net/minecraft/network/syncher/SynchedEntityData.java +++ b/net/minecraft/network/syncher/SynchedEntityData.java @@ -84,7 +84,15 @@ public class SynchedEntityData { @@ -13,7 +13,7 @@ index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..546c36d0bc14b8db49245ff162be3dbc } else { this.isDirty = false; - List> list = new ArrayList<>(); -+ // Leaf start ++ // Leaf start - Optimize SynchedEntityData#packDirty + int cap = 0; + for (SynchedEntityData.DataItem dataItem : this.itemsById) { + if (dataItem.isDirty()) { @@ -21,7 +21,7 @@ index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..546c36d0bc14b8db49245ff162be3dbc + } + } + ArrayList> list = new ArrayList<>(cap); -+ // Leaf end ++ // Leaf end - Optimize SynchedEntityData#packDirty for (SynchedEntityData.DataItem dataItem : this.itemsById) { if (dataItem.isDirty()) { diff --git a/leaf-server/minecraft-patches/features/0174-paw-optimization.patch b/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch similarity index 98% rename from leaf-server/minecraft-patches/features/0174-paw-optimization.patch rename to leaf-server/minecraft-patches/features/0179-Paw-optimization.patch index 48fb02b4..f64e4834 100644 --- a/leaf-server/minecraft-patches/features/0174-paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:36:16 -0400 -Subject: [PATCH] paw optimization +Subject: [PATCH] Paw optimization Some random optimizations @@ -10,10 +10,10 @@ Some random optimizations - Secret patches (WIP) diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index 3a82e1e510029576485427af9fd705b37c5f6e20..32f26640664135c9f7f45f8b204b7ff412fe343e 100644 +index c83ee2137a57e62003b1d20c3ceea9f569350a53..de1f271d36c7daa10c398e146386b51e2622df9a 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -661,13 +661,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -660,13 +660,7 @@ public class Connection extends SimpleChannelInboundHandler> { if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { diff --git a/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch b/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch index 832c367a..2d96ec10 100644 --- a/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch +++ b/leaf-server/paper-patches/features/0024-Multithreaded-Tracker.patch @@ -49,7 +49,7 @@ index edcd209798740f31cb302f36d7864a0d8ea1d561..e2444cc9e28dd432bf3351066b140810 if (entityTracker != null) { - for (ServerPlayerConnection connection : entityTracker.seenBy) { -+ for (ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf ++ for (ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf - Multithreaded tracker players.add(connection.getPlayer().getBukkitEntity()); } } @@ -58,7 +58,7 @@ index edcd209798740f31cb302f36d7864a0d8ea1d561..e2444cc9e28dd432bf3351066b140810 // Paper start - resend possibly desynced entity instead of add entity packet - for (final ServerPlayerConnection connection : entityTracker.seenBy) { -+ for (final ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf ++ for (final ServerPlayerConnection connection : entityTracker.seenBy()) { // Leaf - Multithreaded tracker this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer()); } // Paper end - resend possibly desynced entity instead of add entity packet @@ -67,7 +67,7 @@ index edcd209798740f31cb302f36d7864a0d8ea1d561..e2444cc9e28dd432bf3351066b140810 Set set = new java.util.HashSet<>(tracker.seenBy.size()); - for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy) { -+ for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy()) { // Leaf ++ for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy()) { // Leaf - Multithreaded tracker set.add(connection.getPlayer().getBukkitEntity().getPlayer()); } return set; diff --git a/leaf-server/paper-patches/features/0036-async-chunk-send.patch b/leaf-server/paper-patches/features/0036-Async-chunk-send.patch similarity index 96% rename from leaf-server/paper-patches/features/0036-async-chunk-send.patch rename to leaf-server/paper-patches/features/0036-Async-chunk-send.patch index a0008705..68e26cf6 100644 --- a/leaf-server/paper-patches/features/0036-async-chunk-send.patch +++ b/leaf-server/paper-patches/features/0036-Async-chunk-send.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 2 May 2025 18:22:24 -0700 -Subject: [PATCH] async chunk send +Subject: [PATCH] Async chunk send diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java diff --git a/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch b/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch index e28034f5..ead9dd25 100644 --- a/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch +++ b/leaf-server/paper-patches/features/0037-Optimise-player-movement-checks.patch @@ -5,16 +5,18 @@ Subject: [PATCH] Optimise player movement checks diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java -index 94689e0342cf95dbedec955d67c95fa07a219678..26e4fe123c84e63ae4808facc33d6bbe94fc3133 100644 +index 94689e0342cf95dbedec955d67c95fa07a219678..8a888d52495bd6f447b8d767ca80de20dfee66b9 100644 --- a/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java +++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/SingleUserAreaMap.java -@@ -88,6 +88,9 @@ public abstract class SingleUserAreaMap { +@@ -88,6 +88,11 @@ public abstract class SingleUserAreaMap { if (fromX == NOT_SET) { return false; } ++ // Leaf start - Optimise player movement checks + if (org.dreeam.leaf.config.modules.opt.OptimizePlayerMovementProcessing.enabled && fromX == toX && fromZ == toZ && oldViewDistance == newViewDistance) { + return true; + } ++ // Leaf end - Optimise player movement checks this.lastChunkX = toX; this.lastChunkZ = toZ; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java index d64f225d..d5fcecaf 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSend.java @@ -10,6 +10,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class AsyncChunkSend { + public static final ExecutorService POOL = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java index 7e9f8bcb..4dae48a5 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/chunk/AsyncChunkSendThread.java @@ -1,6 +1,7 @@ package org.dreeam.leaf.async.chunk; public class AsyncChunkSendThread extends Thread { + protected AsyncChunkSendThread(Runnable task) { super(task); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index 9d78ac41..851067ab 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -19,7 +19,6 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; @@ -191,11 +190,11 @@ public class MultithreadedTracker { private static @NotNull ThreadFactory getThreadFactory() { return new ThreadFactoryBuilder() - .setThreadFactory(MultithreadedTrackerThread::new) - .setNameFormat(THREAD_PREFIX + " Thread - %d") - .setPriority(Thread.NORM_PRIORITY - 2) - .setUncaughtExceptionHandler(Util::onThreadException) - .build(); + .setThreadFactory(MultithreadedTrackerThread::new) + .setNameFormat(THREAD_PREFIX + " Thread - %d") + .setPriority(Thread.NORM_PRIORITY - 2) + .setUncaughtExceptionHandler(Util::onThreadException) + .build(); } private static @NotNull RejectedExecutionHandler getRejectedPolicy() { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java index 46f1154a..8176ce30 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/AlternativeJoin.java @@ -14,7 +14,7 @@ public class AlternativeJoin extends ConfigModules { @Override public void onLoaded() { enabled = config.getBoolean(getBasePath() + ".async-switch-state", enabled, config.pickStringRegionBased( - "Async switch connection state", - "...")); + "Async switch connection state.", + "异步切换连接状态.")); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java index 5de82881..ea153f1f 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeItemTicking.java @@ -14,8 +14,8 @@ public class OptimizeItemTicking extends ConfigModules { @Override public void onLoaded() { onlyTickItemsInHand = config.getBoolean(getBasePath() + ".only-tick-items-in-hand", onlyTickItemsInHand, config.pickStringRegionBased(""" - Whether to only tick/update items in main hand and offhand instead of the entire inventory.""", + Whether to only tick / update items in main hand and offhand instead of the entire inventory.""", """ - 是否只对主手和副手中的物品进行tick/更新,而不是整个物品栏中的所有物品。""")); + 是否只对主手和副手中的物品进行 tick / 更新,而不是整个物品栏中的所有物品。""")); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollPackets.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollPackets.java index 458f12c2..7aebf5b6 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollPackets.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollPackets.java @@ -8,6 +8,7 @@ import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.NotNull; public class DoABarrelRollPackets { + private static LeafCustomPayload.@NotNull Type createType(String path) { return new LeafCustomPayload.Type<>(ResourceLocation.fromNamespaceAndPath(DoABarrelRollProtocol.NAMESPACE, path)); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java index 48c2a35d..af88f866 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.OptionalInt; public class DoABarrelRollProtocol implements Protocol { + protected static final String NAMESPACE = "do_a_barrel_roll"; private static final Logger LOGGER = LogManager.getLogger(NAMESPACE); private static final int PROTOCOL_VERSION = 4; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java index 54ca72eb..3894dfa8 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/LeafCustomPayload.java @@ -4,6 +4,7 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import org.jetbrains.annotations.NotNull; public interface LeafCustomPayload extends CustomPacketPayload { + @NotNull @Override Type type(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java index fdedc410..61c5c529 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; interface Protocol { + String namespace(); List> c2s(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java index 4a54b998..000b6f93 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Protocols { + private static final ObjectArrayList PROTOCOLS = new ObjectArrayList<>(); static void register(Protocol protocol) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java index 9887bd89..3a905e30 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/list/HashedReferenceList.java @@ -15,8 +15,8 @@ import java.util.NoSuchElementException; * A List implementation that maintains a hash-based counter for O(1) element lookup. * Combines an array-based list for order with a hash map for fast containment checks. */ -public class HashedReferenceList - implements List { +public class HashedReferenceList implements List { + // The actual ordered storage of elements private final ReferenceArrayList list = new ReferenceArrayList<>(); // Tracks occurrence count of each element for O(1) contains checks diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java index b1fa9212..9d4619c2 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/AttributeInstanceArrayMap.java @@ -10,8 +10,9 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.AbstractMap.SimpleEntry; -/// fast array backend map with O(1) get & put & remove +// fast array backend map with O(1) get & put & remove public class AttributeInstanceArrayMap implements Map, AttributeInstance>, Cloneable { + private int size = 0; private transient AttributeInstance[] a = new AttributeInstance[32]; private transient KeySet keys; From 0a26ea407864152a75c968e4e52eb823d5f008b9 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 21 May 2025 18:57:34 +0800 Subject: [PATCH 73/81] Updated Upstream (Paper/Gale) Upstream has released updates that appear to apply and compile correctly Paper Changes: PaperMC/Paper@09e9afd2 Avoid off-main mutations of state Gale Changes: Dreeam-qwq/Gale@84684276 Updated Upstream (Paper) --- gradle.properties | 2 +- ...0007-Purpur-Server-Minecraft-Changes.patch | 26 +++++++++---------- ...08-Fix-Pufferfish-and-Purpur-patches.patch | 6 ++--- ...Remove-UseItemOnPacket-Too-Far-check.patch | 4 +-- .../0044-Improve-Purpur-AFK-system.patch | 14 +++++----- .../features/0085-Multithreaded-Tracker.patch | 4 +-- .../0164-Reduce-PlayerChunk-Updates.patch | 6 ++--- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/gradle.properties b/gradle.properties index eca97c93..f39575df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ group=cn.dreeam.leaf mcVersion=1.21.4 version=1.21.4-R0.1-SNAPSHOT -galeCommit=00ce862b14008ecbe816606c0843b95d6b227b0d +galeCommit=8468427637dc4f8cc50b03ea117aad3d9cad3e73 org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch index b9c9dfe9..c4764b6e 100644 --- a/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch +++ b/leaf-server/minecraft-patches/features/0007-Purpur-Server-Minecraft-Changes.patch @@ -1316,7 +1316,7 @@ index 060becd4c0b7aa399496ad70ba28cb3449cfaea3..921ad69b699f693e3dfc8d912b0f1a05 this.player.clientBrandName = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256); } diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9abc1db6b2 100644 +index d9473190cfe8930df28ae847a6e7a55b97db58dd..166454f24b46c6d0e9b1e52cf9f88212859476d2 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -328,6 +328,20 @@ public class ServerGamePacketListenerImpl @@ -1370,7 +1370,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause return; } -@@ -1184,6 +1207,10 @@ public class ServerGamePacketListenerImpl +@@ -1185,6 +1208,10 @@ public class ServerGamePacketListenerImpl final int maxBookPageSize = pageMax.intValue(); final double multiplier = Math.clamp(io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier, 0.3D, 1D); long byteAllowed = maxBookPageSize; @@ -1381,7 +1381,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a for (final String page : pageList) { final int byteLength = page.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; byteTotal += byteLength; -@@ -1208,7 +1235,8 @@ public class ServerGamePacketListenerImpl +@@ -1209,7 +1236,8 @@ public class ServerGamePacketListenerImpl } if (byteTotal > byteAllowed) { @@ -1391,7 +1391,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect return; } -@@ -1227,31 +1255,45 @@ public class ServerGamePacketListenerImpl +@@ -1228,31 +1256,45 @@ public class ServerGamePacketListenerImpl Optional optional = packet.title(); optional.ifPresent(list::add); list.addAll(packet.pages()); @@ -1441,7 +1441,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a itemStack.set( DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getName().getString(), 0, list, true) -@@ -1265,6 +1307,16 @@ public class ServerGamePacketListenerImpl +@@ -1266,6 +1308,16 @@ public class ServerGamePacketListenerImpl return this.player.isTextFilteringEnabled() ? Filterable.passThrough(filteredText.filteredOrEmpty()) : Filterable.from(filteredText); } @@ -1458,7 +1458,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a @Override public void handleEntityTagQuery(ServerboundEntityTagQueryPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); -@@ -1300,7 +1352,15 @@ public class ServerGamePacketListenerImpl +@@ -1301,7 +1353,15 @@ public class ServerGamePacketListenerImpl @Override public void handleMovePlayer(ServerboundMovePlayerPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1475,7 +1475,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause } else { ServerLevel serverLevel = this.player.serverLevel(); -@@ -1475,7 +1535,7 @@ public class ServerGamePacketListenerImpl +@@ -1476,7 +1536,7 @@ public class ServerGamePacketListenerImpl movedWrongly = true; if (event.getLogWarning()) // Paper end @@ -1484,7 +1484,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a } // Paper } -@@ -1541,6 +1601,8 @@ public class ServerGamePacketListenerImpl +@@ -1542,6 +1602,8 @@ public class ServerGamePacketListenerImpl this.lastYaw = to.getYaw(); this.lastPitch = to.getPitch(); @@ -1493,7 +1493,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a Location oldTo = to.clone(); PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); this.cserver.getPluginManager().callEvent(event); -@@ -1597,6 +1659,13 @@ public class ServerGamePacketListenerImpl +@@ -1598,6 +1660,13 @@ public class ServerGamePacketListenerImpl this.player.tryResetCurrentImpulseContext(); } @@ -1507,7 +1507,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a this.player.checkMovementStatistics(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z); this.lastGoodX = this.player.getX(); this.lastGoodY = this.player.getY(); -@@ -1645,6 +1714,17 @@ public class ServerGamePacketListenerImpl +@@ -1646,6 +1715,17 @@ public class ServerGamePacketListenerImpl } } @@ -1525,7 +1525,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a // Paper start - optimise out extra getCubes private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { final List collisionsBB = new java.util.ArrayList<>(); -@@ -2015,6 +2095,7 @@ public class ServerGamePacketListenerImpl +@@ -2016,6 +2096,7 @@ public class ServerGamePacketListenerImpl boolean cancelled; if (hitResult == null || hitResult.getType() != HitResult.Type.BLOCK) { @@ -1533,7 +1533,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemInHand, hand); cancelled = event.useItemInHand() == Event.Result.DENY; } else { -@@ -2758,6 +2839,7 @@ public class ServerGamePacketListenerImpl +@@ -2759,6 +2840,7 @@ public class ServerGamePacketListenerImpl AABB boundingBox = target.getBoundingBox(); if (this.player.canInteractWithEntity(boundingBox, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience value for interact range @@ -1541,7 +1541,7 @@ index a3d0d331d367b8ddfd0ac450acd143ce7d3f7a9a..5fcd389b5483c4c11e7a007b2b6abb9a packet.dispatch( new ServerboundInteractPacket.Handler() { private void performInteraction(InteractionHand hand, ServerGamePacketListenerImpl.EntityInteraction entityInteraction, PlayerInteractEntityEvent event) { // CraftBukkit -@@ -2770,6 +2852,8 @@ public class ServerGamePacketListenerImpl +@@ -2771,6 +2853,8 @@ public class ServerGamePacketListenerImpl ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); diff --git a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch index 1e985b04..7f2591b9 100644 --- a/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch +++ b/leaf-server/minecraft-patches/features/0008-Fix-Pufferfish-and-Purpur-patches.patch @@ -78,10 +78,10 @@ index 55f708438e5d71cf14f4e632fc20a65b4bfb7d25..54895ed9ad9b9b2c4c12cfcce89af453 // Gale end - MultiPaper - skip unnecessary mob spawning computations int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 1ba275fd76970774922bcd91bea6bf779bab32b7..a96f4f45d465b6f0e5b061877bd11f3caeeb1625 100644 +index 166454f24b46c6d0e9b1e52cf9f88212859476d2..cd9793763c9e804b25bf9bcf19bc9a401258166f 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1235,7 +1235,7 @@ public class ServerGamePacketListenerImpl +@@ -1236,7 +1236,7 @@ public class ServerGamePacketListenerImpl } if (byteTotal > byteAllowed) { @@ -142,7 +142,7 @@ index a5a8bdecddadac3de1b5a0c1a9849ce1cd52a530..f500f4e32e676712fcd0c877498acc27 public Entity(EntityType entityType, Level level) { this.type = entityType; diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index ac006d8738592bc5cb77033adc8c442ce302a476..ce2a9b6c4b903e5285e77f7ee64defd8e81029ae 100644 +index e66ad09280d8fb448953a6204d9fd81913227219..9124ba3ea2fca0ffa105147db1377a0dd7b818e0 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java @@ -1024,13 +1024,13 @@ public abstract class LivingEntity extends Entity implements Attackable { diff --git a/leaf-server/minecraft-patches/features/0013-Remove-UseItemOnPacket-Too-Far-check.patch b/leaf-server/minecraft-patches/features/0013-Remove-UseItemOnPacket-Too-Far-check.patch index 917d8f1f..f21c3315 100644 --- a/leaf-server/minecraft-patches/features/0013-Remove-UseItemOnPacket-Too-Far-check.patch +++ b/leaf-server/minecraft-patches/features/0013-Remove-UseItemOnPacket-Too-Far-check.patch @@ -7,10 +7,10 @@ This Check is added in 1.17.x -> 1.18.x that updated by Mojang. By removing this check, it gives ability for hackers to use some modules of hack clients. diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index a96f4f45d465b6f0e5b061877bd11f3caeeb1625..61bf3bfb41392d38c7e796f56cc0cce870e12631 100644 +index cd9793763c9e804b25bf9bcf19bc9a401258166f..6fbed156d25a068e8085934328ffab961ffd08d3 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2013,8 +2013,13 @@ public class ServerGamePacketListenerImpl +@@ -2014,8 +2014,13 @@ public class ServerGamePacketListenerImpl BlockPos blockPos = hitResult.getBlockPos(); if (this.player.canInteractWithBlock(blockPos, 1.0)) { Vec3 vec3 = location.subtract(Vec3.atCenterOf(blockPos)); diff --git a/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch b/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch index fc454c68..8a0cbecf 100644 --- a/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch +++ b/leaf-server/minecraft-patches/features/0044-Improve-Purpur-AFK-system.patch @@ -53,10 +53,10 @@ index 16a6aba187fa00fd7c3f739e46bc632987c1378f..8a9c2489730dd47cc776493695393e78 } else { getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix, true); diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041053082e8 100644 +index a902fd7c663ce588065d4b23ca4b1d48163e8977..4c39a5d0ee3bf532fd536884232df542154e8a48 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2272,6 +2272,7 @@ public class ServerGamePacketListenerImpl +@@ -2273,6 +2273,7 @@ public class ServerGamePacketListenerImpl @Override public void handleChatCommand(ServerboundChatCommandPacket packet) { @@ -64,7 +64,7 @@ index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041 this.tryHandleChat(packet.command(), () -> { // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands if (this.player.hasDisconnected()) { -@@ -2280,7 +2281,7 @@ public class ServerGamePacketListenerImpl +@@ -2281,7 +2282,7 @@ public class ServerGamePacketListenerImpl // CraftBukkit end this.performUnsignedChatCommand(packet.command()); this.detectRateSpam("/" + packet.command()); // Spigot @@ -73,7 +73,7 @@ index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041 } private void performUnsignedChatCommand(String command) { -@@ -2313,6 +2314,7 @@ public class ServerGamePacketListenerImpl +@@ -2314,6 +2315,7 @@ public class ServerGamePacketListenerImpl public void handleSignedChatCommand(ServerboundChatCommandSignedPacket packet) { Optional optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); if (!optional.isEmpty()) { @@ -81,7 +81,7 @@ index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041 this.tryHandleChat(packet.command(), () -> { // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands if (this.player.hasDisconnected()) { -@@ -2321,7 +2323,7 @@ public class ServerGamePacketListenerImpl +@@ -2322,7 +2324,7 @@ public class ServerGamePacketListenerImpl // CraftBukkit end this.performSignedChatCommand(packet, optional.get()); this.detectRateSpam("/" + packet.command()); // Spigot @@ -90,7 +90,7 @@ index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041 } } -@@ -2426,12 +2428,17 @@ public class ServerGamePacketListenerImpl +@@ -2427,12 +2429,17 @@ public class ServerGamePacketListenerImpl return dispatcher.parse(command, this.player.createCommandSourceStack()); } @@ -110,7 +110,7 @@ index 594ff2cf2a1f9eea2fb99997383af6d78dc424fb..ddd8a7a9b33618979ff0b69a95ce8041 this.player.resetLastActionTime(); // CraftBukkit start if (sync) { -@@ -2443,6 +2450,40 @@ public class ServerGamePacketListenerImpl +@@ -2444,6 +2451,40 @@ public class ServerGamePacketListenerImpl } } diff --git a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch index a15a8293..b0a5032c 100644 --- a/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0085-Multithreaded-Tracker.patch @@ -373,10 +373,10 @@ index 275b640f4536366152f59acf071dd4eba15696c8..a669a59a42f814480879a52d2da5e04c } } diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 04bf8bba0d8c0d5459605253dcc3f135bf43fd95..abe79d07196de0a10a382d4c37161c7eb4a604ae 100644 +index 327b3bc89920c4ab02c1126dc63bca05ce3abefe..1415043bee5fbbfcf9dab9184a9418d52f531f62 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1819,7 +1819,7 @@ public class ServerGamePacketListenerImpl +@@ -1820,7 +1820,7 @@ public class ServerGamePacketListenerImpl } public void internalTeleport(PositionMoveRotation posMoveRotation, Set relatives) { diff --git a/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch index 8a4ce281..db0736c3 100644 --- a/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch +++ b/leaf-server/minecraft-patches/features/0164-Reduce-PlayerChunk-Updates.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Reduce PlayerChunk Updates diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index abe79d07196de0a10a382d4c37161c7eb4a604ae..51079419496900b86ff5e9ed41150831681e6d0b 100644 +index 1415043bee5fbbfcf9dab9184a9418d52f531f62..1a09ea1953169b72d67126283b5f581c2797506a 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1367,6 +1367,8 @@ public class ServerGamePacketListenerImpl +@@ -1368,6 +1368,8 @@ public class ServerGamePacketListenerImpl this.resetPosition(); } @@ -17,7 +17,7 @@ index abe79d07196de0a10a382d4c37161c7eb4a604ae..51079419496900b86ff5e9ed41150831 if (!this.updateAwaitingTeleport() && this.player.hasClientLoaded()) { double d = clampHorizontal(packet.getX(this.player.getX())); final double toX = d; // Paper - OBFHELPER double d1 = clampVertical(packet.getY(this.player.getY())); final double toY = d1; // Paper - OBFHELPER -@@ -1638,7 +1640,7 @@ public class ServerGamePacketListenerImpl +@@ -1639,7 +1641,7 @@ public class ServerGamePacketListenerImpl && !isFallFlying && !isAutoSpinAttack && this.noBlocksAround(this.player); From e5da1b1038a59910f22cfb594836312dd8096e15 Mon Sep 17 00:00:00 2001 From: LingXD <90316914+wling-art@users.noreply.github.com> Date: Wed, 21 May 2025 23:54:32 +0800 Subject: [PATCH 74/81] Optimize isEyeInFluid (#327) * Optimize isEyeInFluid * Remove accidental formatting * Fix code removed by oblivion * Optimize isEyeInFluid * Update after rebase --- .../features/0179-Optimize-isEyeInFluid.patch | 77 +++++++++++++++++++ ...tion.patch => 0180-Paw-optimization.patch} | 12 +-- .../config/modules/opt/EyeFluidCache.java | 21 +++++ 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch rename leaf-server/minecraft-patches/features/{0179-Paw-optimization.patch => 0180-Paw-optimization.patch} (96%) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EyeFluidCache.java diff --git a/leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch b/leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch new file mode 100644 index 00000000..a5b9e95d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0179-Optimize-isEyeInFluid.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wling-art +Date: Sat, 17 May 2025 08:25:33 +0800 +Subject: [PATCH] Optimize isEyeInFluid + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 5eb740476dff894e91c2bd779ef6b760213a78a5..64f24d3e0ecb91e0b4df6229354aeac549234f1b 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -262,6 +262,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + protected Object2DoubleMap> fluidHeight = new Object2DoubleArrayMap<>(2); + protected boolean wasEyeInWater; + private final Set> fluidOnEyes = new HashSet<>(); ++ private static final int FLUID_WATER = 1; // Leaf - Optimize isEyeInFluid ++ private static final int FLUID_LAVA = 2; // Leaf - Optimize isEyeInFluid ++ private int fluidCache = 0; // Leaf - Optimize isEyeInFluid + public int invulnerableTime; + protected boolean firstTick = true; + protected final SynchedEntityData entityData; +@@ -2022,7 +2025,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + private void updateFluidOnEyes() { + this.wasEyeInWater = this.isEyeInFluid(FluidTags.WATER); +- this.fluidOnEyes.clear(); ++ if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) fluidCache = 0; else this.fluidOnEyes.clear(); // Leaf - Optimize isEyeInFluid ++ + double eyeY = this.getEyeY(); + if (!( + this.getVehicle() instanceof AbstractBoat abstractBoat +@@ -2034,7 +2038,18 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + FluidState fluidState = this.level().getFluidState(blockPos); + double d = blockPos.getY() + fluidState.getHeight(this.level(), blockPos); + if (d > eyeY) { +- this.fluidOnEyes.addAll(fluidState.getTagsAsSet()); // Leaf - Remove stream in updateFluidOnEyes ++ // Leaf start - Optimize isEyeInFluid ++ if (org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) { ++ if (fluidState.is(FluidTags.WATER)) { ++ setFluidStatus(FluidTags.WATER, true); ++ } ++ if (fluidState.is(FluidTags.LAVA)) { ++ setFluidStatus(FluidTags.LAVA, true); ++ } ++ } else { ++ this.fluidOnEyes.addAll(fluidState.getTagsAsSet()); // Leaf - Remove stream in updateFluidOnEyes ++ } ++ // Leaf end - Optimize isEyeInFluid + } + } + } +@@ -2114,9 +2129,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + ++ // Leaf start - Optimize isEyeInFluid + public boolean isEyeInFluid(TagKey fluidTag) { +- return this.fluidOnEyes.contains(fluidTag); ++ if (!org.dreeam.leaf.config.modules.opt.EyeFluidCache.enabled) { ++ return this.fluidOnEyes.contains(fluidTag); ++ } ++ return fluidTag == FluidTags.WATER ? (fluidCache & FLUID_WATER) != 0 ++ : fluidTag == FluidTags.LAVA && (fluidCache & FLUID_LAVA) != 0; ++ } ++ ++ public void setFluidStatus(TagKey fluidTag, boolean isInFluid) { ++ int bit = fluidTag == FluidTags.WATER ? FLUID_WATER ++ : fluidTag == FluidTags.LAVA ? FLUID_LAVA ++ : 0; ++ ++ if (bit == 0) return; ++ ++ fluidCache = isInFluid ? (fluidCache | bit) : (fluidCache & ~bit); + } ++ // Leaf end - Optimize isEyeInFluid + + public boolean isInLava() { + return !this.firstTick && this.fluidHeight.getDouble(FluidTags.LAVA) > 0.0; diff --git a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0180-Paw-optimization.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0179-Paw-optimization.patch rename to leaf-server/minecraft-patches/features/0180-Paw-optimization.patch index f64e4834..10785958 100644 --- a/leaf-server/minecraft-patches/features/0179-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0180-Paw-optimization.patch @@ -126,10 +126,10 @@ index e6fd46b8148e050c4807abf6c8a03e4747bc0da2..0d8b71bbe5835187d5dfc1a301b6a01b 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 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5178e070c 100644 +index 64f24d3e0ecb91e0b4df6229354aeac549234f1b..df23d80d6b18e900414aa02e5c1812f0a10f0fb7 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -1142,31 +1142,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1145,31 +1145,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return this.onGround; } @@ -161,7 +161,7 @@ index 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5 public void move(MoverType type, Vec3 movement) { // Gale start - VMP - skip entity move if movement is zero if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { -@@ -1174,16 +1149,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1177,16 +1152,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Gale end - VMP - skip entity move if movement is zero final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity @@ -178,7 +178,7 @@ index 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5 if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { -@@ -1304,13 +1270,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -1307,13 +1273,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Gale end - skip negligible planar movement multiplication } } @@ -192,7 +192,7 @@ index 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5 } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4848,9 +4807,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4879,9 +4838,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -202,7 +202,7 @@ index 5eb740476dff894e91c2bd779ef6b760213a78a5..e8a24178cb74afee749bfb067ac010f5 } public void addDeltaMovement(Vec3 addend) { -@@ -4956,9 +4913,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4987,9 +4944,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Fix MC-4 if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EyeFluidCache.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EyeFluidCache.java new file mode 100644 index 00000000..36f3fdb9 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/EyeFluidCache.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class EyeFluidCache extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".cache-eye-fluid-status", enabled, + config.pickStringRegionBased( + "Whether to cache the isEyeInFluid method to improve performance and reduce memory usage.", + "是否为 isEyeInFluid 方法启用缓存,以优化性能并减少内存使用.")); + } +} From 727a0827bc1acc5e1ba6e41b0e390d7ad955033b Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Thu, 22 May 2025 23:15:10 +0900 Subject: [PATCH 75/81] remove MultithreadedTracker lambda --- .../leaf/async/tracker/MultithreadedTracker.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index 851067ab..a62ea7a9 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -32,6 +32,15 @@ public class MultithreadedTracker { private static long lastWarnMillis = System.currentTimeMillis(); private static ThreadPoolExecutor TRACKER_EXECUTOR = null; + private record SendChanges(Object[] entities, int size) implements Runnable { + @Override + public void run() { + for (int i = 0; i < size; i++) { + ((ServerEntity) entities[i]).sendDirtyEntityData(); + } + } + } + private MultithreadedTracker() { } @@ -90,7 +99,7 @@ public class MultithreadedTracker { } } if (!sendDirty.isEmpty()) { - level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData)); + level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size())); } }); } @@ -131,6 +140,7 @@ public class MultithreadedTracker { sendChanges.run(); } + ReferenceArrayList sendDirty = new ReferenceArrayList<>(); for (final Entity entity : trackerEntitiesRaw) { if (entity == null) continue; @@ -144,7 +154,7 @@ public class MultithreadedTracker { } } if (!sendDirty.isEmpty()) { - level.getServer().execute(() -> sendDirty.forEach(ServerEntity::sendDirtyEntityData)); + level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size())); } }); } From 6902af834a41edb9a5d478a101342d1d0a7477ee Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 23 May 2025 12:02:53 +0900 Subject: [PATCH 76/81] Cache block path type --- .../features/0181-Cache-block-path-type.patch | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch diff --git a/leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch b/leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch new file mode 100644 index 00000000..87f5212b --- /dev/null +++ b/leaf-server/minecraft-patches/features/0181-Cache-block-path-type.patch @@ -0,0 +1,122 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 23 May 2025 12:01:42 +0900 +Subject: [PATCH] Cache block path type + + +diff --git a/net/minecraft/server/Bootstrap.java b/net/minecraft/server/Bootstrap.java +index 35b91f4daba4ac9751fa388d9da7d127db1356b0..60cc058db9227a61cc3186003efefc4888e06ddc 100644 +--- a/net/minecraft/server/Bootstrap.java ++++ b/net/minecraft/server/Bootstrap.java +@@ -60,6 +60,7 @@ public class Bootstrap { + io.papermc.paper.world.worldgen.OptionallyFlatBedrockConditionSource.bootstrap(); // Paper - Flat bedrock generator settings + }); + // Paper end ++ net.minecraft.world.level.block.Blocks.initPathType(); // Leaf - Cache path type + CreativeModeTabs.validate(); + wrapStreams(); + bootstrapDuration.set(Duration.between(instant, Instant.now()).toMillis()); +diff --git a/net/minecraft/world/level/block/Blocks.java b/net/minecraft/world/level/block/Blocks.java +index 07a8fbfa7eb6e684ea699f009ce2d19311994e39..c5840aad8b6a719873b06a6c5e30cba35555656f 100644 +--- a/net/minecraft/world/level/block/Blocks.java ++++ b/net/minecraft/world/level/block/Blocks.java +@@ -6954,4 +6954,14 @@ public class Blocks { + } + } + } ++ ++ // Leaf start - Cache path type ++ public static void initPathType() { ++ for (Block block : BuiltInRegistries.BLOCK) { ++ for (BlockState blockState : block.getStateDefinition().getPossibleStates()) { ++ blockState.initPathType(); ++ } ++ } ++ } ++ // Leaf end - Cache path type + } +diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java +index 3a49690672bc49259e863e5cde7f14d57c89fcd5..d498ee864362e378c430d0d2916d76cf8e69f069 100644 +--- a/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -476,6 +476,7 @@ public abstract class BlockBehaviour implements FeatureElement { + private boolean emptyCollisionShape; + private boolean emptyConstantCollisionShape; + private VoxelShape constantCollisionShape; ++ public net.minecraft.world.level.pathfinder.PathType pathType; // Leaf - Cache path type + + private static void initCaches(final VoxelShape shape, final boolean neighbours) { + ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$isFullBlock(); +@@ -645,6 +646,12 @@ public abstract class BlockBehaviour implements FeatureElement { + // Paper end - optimise collisions + } + ++ // Leaf start - Cache path type ++ public void initPathType() { ++ pathType = net.minecraft.world.level.pathfinder.WalkNodeEvaluator.getPathTypeFromState(this.asState()); ++ } ++ // Leaf end - Cache path type ++ + public Block getBlock() { + return this.owner; + } +diff --git a/net/minecraft/world/level/pathfinder/PathTypeCache.java b/net/minecraft/world/level/pathfinder/PathTypeCache.java +index 3b190ba2a719cc45045d9be893884b50b9364b58..b44f6562a7f7651b2728ba49699031000b54b818 100644 +--- a/net/minecraft/world/level/pathfinder/PathTypeCache.java ++++ b/net/minecraft/world/level/pathfinder/PathTypeCache.java +@@ -24,7 +24,7 @@ public class PathTypeCache { + } + + private PathType compute(BlockGetter level, BlockPos pos, int index, long packedPos) { +- PathType pathTypeFromState = WalkNodeEvaluator.getPathTypeFromState(level, pos); ++ PathType pathTypeFromState = WalkNodeEvaluator.leafPathType(level, pos); // Leaf - Cache path type + this.positions[index] = packedPos; + this.pathTypes[index] = pathTypeFromState; + return pathTypeFromState; +diff --git a/net/minecraft/world/level/pathfinder/PathfindingContext.java b/net/minecraft/world/level/pathfinder/PathfindingContext.java +index 4eca1dd0819c7ee7a77e45fc5fa03f4ee5cdceaf..ff68b19b5fe4c80872d483363611b843146de989 100644 +--- a/net/minecraft/world/level/pathfinder/PathfindingContext.java ++++ b/net/minecraft/world/level/pathfinder/PathfindingContext.java +@@ -27,7 +27,7 @@ public class PathfindingContext { + + public PathType getPathTypeFromState(int x, int y, int z) { + BlockPos blockPos = this.mutablePos.set(x, y, z); +- return this.cache == null ? WalkNodeEvaluator.getPathTypeFromState(this.level, blockPos) : this.cache.getOrCompute(this.level, blockPos); ++ return this.cache == null ? WalkNodeEvaluator.leafPathType(this.level, blockPos) : this.cache.getOrCompute(this.level, blockPos); // Leaf - Cache path type + } + + public BlockState getBlockState(BlockPos pos) { +diff --git a/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +index db6baaa698fe93aba3fbd595158b568badd6cb8a..5bd1300ef82a1d431c098d9cf75a7fa9d5a43502 100644 +--- a/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +@@ -479,6 +479,16 @@ public class WalkNodeEvaluator extends NodeEvaluator { + return pathType; + } + ++ // Leaf start - Cache path type ++ public static PathType leafPathType(BlockGetter level, BlockPos blockPos) { ++ BlockState blockState = level.getBlockStateIfLoaded(blockPos); ++ if (blockState == null) { ++ return PathType.BLOCKED; ++ } ++ return blockState.pathType; ++ } ++ // Leaf end - Cache path type ++ + protected static PathType getPathTypeFromState(BlockGetter level, BlockPos pos) { + // Paper start - Do not load chunks during pathfinding + BlockState blockState = level.getBlockStateIfLoaded(pos); +@@ -486,6 +496,12 @@ public class WalkNodeEvaluator extends NodeEvaluator { + return PathType.BLOCKED; + } + // Paper end ++ // Leaf start - Cache path type ++ return getPathTypeFromState(blockState); ++ } ++ ++ public static PathType getPathTypeFromState(BlockState blockState) { ++ // Leaf end - Cache path type + Block block = blockState.getBlock(); + if (blockState.isAir()) { + return PathType.OPEN; From 1c6aa6076986ae5bb5235653a80cb3e57af96086 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 23 May 2025 15:19:18 +0900 Subject: [PATCH 77/81] optimize GoalSelector update order --- .../features/0153-Async-target-finding.patch | 397 +++++++++++++----- .../features/0038-Async-target-finding.patch | 30 ++ .../leaf/async/ai/AsyncGoalExecutor.java | 15 +- .../java/org/dreeam/leaf/async/ai/Waker.java | 3 + 4 files changed, 325 insertions(+), 120 deletions(-) create mode 100644 leaf-server/paper-patches/features/0038-Async-target-finding.patch diff --git a/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch index fd386af4..eda41928 100644 --- a/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch @@ -237,7 +237,7 @@ index 9af7dafe03812d96aa477584d4147a68c240ab21..e6fd46b8148e050c4807abf6c8a03e47 // Paper start - log detailed entity tick information diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd269268800516c426 100644 +index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..44d167e9c92260e7e5a134d7dae310b90d5cd7ff 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -144,6 +144,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -253,12 +253,17 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd26926880 protected Mob(EntityType entityType, Level level) { super(entityType, level); -@@ -225,12 +231,22 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -225,12 +231,27 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab } // Paper end - Skip AI during inactive ticks for non-aware mobs boolean isThrottled = org.dreeam.leaf.config.modules.opt.ThrottleInactiveGoalSelectorTick.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking + // Leaf start - Async target finding -+ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; ++ boolean hasTasks; ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ hasTasks = ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this); ++ } else { ++ hasTasks = true; ++ } + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); @@ -268,20 +273,25 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd26926880 this.targetSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { -+ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); ++ if (!hasTasks && ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this)) { ++ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this); + } + } + // Leaf end - Async target finding } // Paper end -@@ -914,17 +930,29 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -914,17 +935,34 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab // Paper end - Allow nerfed mobs to jump and float this.sensing.tick(); int i = this.tickCount + this.getId(); + // Leaf start - Async target finding -+ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; ++ boolean hasTasks; ++ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { ++ hasTasks = ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this); ++ } else { ++ hasTasks = true; ++ } if (i % 2 != 0 && this.tickCount > 1) { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking @@ -298,8 +308,8 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd26926880 this.goalSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { -+ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); ++ if (!hasTasks && ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this)) { ++ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this); + } + } + // Leaf end - Async target finding @@ -668,17 +678,34 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 return false; } else { this.parent = animal; +diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java +index 5f5bf0e710ecff09a571091e5a923332be70cb74..487da7485bb957ef6516c2950035ca304b84edd7 100644 +--- a/net/minecraft/world/entity/ai/goal/Goal.java ++++ b/net/minecraft/world/entity/ai/goal/Goal.java +@@ -100,6 +100,7 @@ public abstract class Goal { + // Paper end - Mob goal api + + public static enum Flag { ++ // Leaf - Async target finding - inline diff + UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR + MOVE, + LOOK, diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb506310de8461 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..4811081a1ec32432958473f8f58b1a1cde76a2cd 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -26,13 +26,23 @@ public class GoalSelector { +@@ -26,13 +26,28 @@ public class GoalSelector { private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 + // Leaf start - Async target finding -+ private boolean availableGoalsDirty = true; -+ private WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; ++ private boolean ctxDirty = true; ++ private WrappedGoal[] ctxGoals = {}; ++ private int[] ctxRunning = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; ++ private int[] ctxNonRunning = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; ++ private int[] ctxLockedFlags = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; ++ private int ctxRunningLength = 0; ++ private int ctxNonRunningLength = 0; + private int ctxIndex = 0; + public int ctxState = -1; + public final org.dreeam.leaf.async.ai.Waker ctx = new org.dreeam.leaf.async.ai.Waker(); @@ -686,21 +713,21 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 + public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); -+ availableGoalsDirty = true; // Leaf - Async target finding ++ ctxDirty = true; // Leaf - Async target finding } @VisibleForTesting public void removeAllGoals(Predicate filter) { this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); -+ availableGoalsDirty = true; // Leaf - Async target finding ++ ctxDirty = true; // Leaf - Async target finding } // Paper start - EAR 2 -@@ -63,18 +73,19 @@ public class GoalSelector { +@@ -63,18 +78,19 @@ public class GoalSelector { } this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); -+ availableGoalsDirty = true; // Leaf - Async target finding ++ ctxDirty = true; // Leaf - Async target finding } // Paper start - Perf: optimize goal types @@ -718,130 +745,262 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); // Paper end - Perf: optimize goal types if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { -@@ -85,7 +96,131 @@ public class GoalSelector { +@@ -85,7 +101,263 @@ public class GoalSelector { return true; } + // Leaf start - Async target finding + public final boolean poll() { -+ if (this.ctxGoals == null || ctx.wake != null) { ++ if (ctx.wake != null) { + return false; + } -+ if (ctxState == 0) { -+ while (ctxIndex < this.ctxGoals.length) { -+ WrappedGoal goal = this.ctxGoals[ctxIndex]; -+ // entity tempt -+ if (goal.isRunning() && (goalContainsAnyFlags(goal, this.goalTypes) || !goal.canContinueToUse())) { -+ ctx.state = false; -+ if (ctx.wake != null) { -+ return true; -+ } -+ goal.stop(); -+ } -+ -+ ctxIndex++; -+ ctx.state = true; ++ if (ctxState >= 65536) { ++ ctxState -= 65536; ++ ctxIndex = ctxRunningLength; ++ ctx.state = true; ++ if (ctxDirty) { ++ stateInit(); + } -+ -+ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); -+ -+ ctxIndex = 0; ++ } ++ if (ctxState == 0) { ++ if (stateCanContinueToUseStop()) return true; ++ ctxIndex = ctxNonRunningLength; + ctx.state = true; + ctxState = 1; + } + if (ctxState == 1) { -+ while (ctxIndex < this.ctxGoals.length) { -+ WrappedGoal goal = this.ctxGoals[ctxIndex]; -+ var flags = goal.getFlags(); -+ // entity and block -+ if (!goal.isRunning() && !flags.hasCommonElements(this.goalTypes)) { -+ // inline -+ boolean result = true; -+ long flagIterator1 = flags.getBackingSet(); -+ int wrappedGoalSize1 = flags.size(); -+ for (int i1 = 0; i1 < wrappedGoalSize1; ++i1) { -+ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator1)]; -+ flagIterator1 ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator1); -+ if (!this.lockedFlags.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { -+ result = false; -+ break; -+ } -+ } -+ if (result) { -+ if (goal.canUse()) { -+ long flagIterator = flags.getBackingSet(); -+ int wrappedGoalSize = flags.size(); -+ for (int i = 0; i < wrappedGoalSize; ++i) { -+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; -+ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); -+ WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); -+ wrappedGoal1.stop(); -+ this.lockedFlags.put(flag, goal); -+ } -+ -+ goal.start(); -+ } -+ ctx.state = false; -+ if (ctx.wake != null) { -+ return true; -+ } -+ } -+ } -+ // entity alert other -+ if (!ctx.state) { -+ switch (goal.getGoal()) { -+ case net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal t -> t.poll(); -+ case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); -+ default -> {} -+ } -+ } -+ ctxIndex++; -+ ctx.state = true; -+ } -+ -+ ctxIndex = 0; ++ if (stateCanUseStart()) return true; ++ ctxIndex = ctxRunningLength; + ctx.state = true; + ctxState = 2; + } + if (ctxState == 2) { -+ while (ctxIndex < this.ctxGoals.length) { -+ WrappedGoal goal = this.ctxGoals[ctxIndex]; -+ if (goal.isRunning()) { -+ goal.tick(); -+ } -+ ctxIndex++; -+ } -+ ++ stateTickRunningAll(); + ctxState = -1; -+ ctxIndex = 0; -+ ctx.state = true; + } + if (ctxState == 3) { -+ while (ctxIndex < this.ctxGoals.length) { -+ WrappedGoal goal = this.ctxGoals[ctxIndex]; -+ if (goal.isRunning() && goal.requiresUpdateEveryTick()) { -+ goal.tick(); ++ stateTickRunningPartial(); ++ ctxState = -1; ++ } ++ return false; ++ } ++ ++ private void stateInit() { ++ ctxDirty = false; ++ { ++ if (availableGoals.size() > ctxGoals.length) { ++ ctxGoals = new WrappedGoal[availableGoals.size()]; ++ } ++ int i = 0; ++ for (WrappedGoal goal : this.availableGoals) { ++ ctxGoals[i] = goal; ++ i += 1; ++ } ++ for (; i < ctxGoals.length; i++) { ++ ctxGoals[i] = NO_GOAL; ++ } ++ } ++ if (ctxGoals.length > ctxRunning.length) { ++ ctxRunning = new int[ctxGoals.length]; ++ } ++ if (ctxGoals.length > ctxNonRunning.length) { ++ ctxNonRunning = new int[ctxGoals.length]; ++ } ++ ctxRunningLength = 0; ++ ctxNonRunningLength = 0; ++ for (int i = 0; i < ctxGoals.length; i++) { ++ final WrappedGoal goal = ctxGoals[i]; ++ if (goal.isRunning()) { ++ ctxRunning[ctxRunningLength] = i; ++ ctxRunningLength += 1; ++ } else { ++ ctxNonRunning[ctxNonRunningLength] = i; ++ ctxNonRunningLength += 1; ++ } ++ } ++ it.unimi.dsi.fastutil.ints.IntArrays.quickSort(ctxRunning, 0, ctxRunningLength, (i, j) -> Integer.compare(ctxGoals[j].getPriority(), ctxGoals[i].getPriority())); ++ it.unimi.dsi.fastutil.ints.IntArrays.quickSort(ctxNonRunning, 0, ctxNonRunningLength, (i, j) -> Integer.compare(ctxGoals[j].getPriority(), ctxGoals[i].getPriority())); ++ ++ if (ctxLockedFlags.length == 0) { ++ ctxLockedFlags = new int[GOAL_FLAG_VALUES.length]; ++ } ++ for (int i = 0; i < GOAL_FLAG_VALUES.length; i++) { ++ final WrappedGoal goal = lockedFlags.get(GOAL_FLAG_VALUES[i]); ++ if (goal != null) { ++ if (goal.isInterruptable()) { ++ ctxLockedFlags[i] = goal.getPriority(); ++ } else { ++ ctxLockedFlags[i] = -1; + } -+ ctxIndex++; ++ } else { ++ ctxLockedFlags[i] = Integer.MAX_VALUE; ++ } ++ } ++ } ++ ++ ++ private boolean stateCanContinueToUseStop() { ++ while (ctxIndex != 0) { ++ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; ++ final int flags = (int) goal.getFlags().backingSet; ++ boolean remove; ++ // entity TemptGoal ++ if ((((int) this.goalTypes.backingSet) & flags) != 0 || !goal.isRunning()) { ++ goal.stop(); ++ remove = true; ++ } else if (!goal.canContinueToUse()) { ++ if (ctx.wake != null) { ++ ctx.state = false; ++ return true; ++ } ++ goal.stop(); ++ remove = true; ++ } else { ++ remove = false; + } + -+ ctxState = -1; -+ ctxIndex = 0; ++ if (remove) { ++ ctxNonRunning[ctxNonRunningLength] = ctxRunning[ctxIndex - 1]; ++ ctxNonRunningLength++; ++ if (ctxIndex != ctxRunningLength) { ++ ctxRunning[ctxIndex - 1] = ctxRunning[ctxRunningLength - 1]; ++ } ++ ctxRunningLength--; ++ } ++ ctxIndex--; ++ ctx.state = true; ++ } ++ ++ for (int i = 0; i < GOAL_FLAG_VALUES.length; i++) { ++ final WrappedGoal goal = lockedFlags.get(GOAL_FLAG_VALUES[i]); ++ if (goal != null && !goal.isRunning()) { ++ lockedFlags.remove(GOAL_FLAG_VALUES[i]); ++ ctxLockedFlags[i] = Integer.MAX_VALUE; ++ } ++ } ++ return false; ++ } ++ ++ private boolean stateCanUseStart() { ++ while (ctxIndex != 0) { ++ boolean remove; ++ final WrappedGoal goal = ctxGoals[ctxNonRunning[ctxIndex - 1]]; ++ final int flags = (int) goal.getFlags().backingSet; ++ if (goal.isRunning()) { ++ // entity alertOther ++ if (!ctx.state) { ++ switch (goal.getGoal()) { ++ case net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal t -> t.poll(); ++ case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); ++ default -> { ++ } ++ } ++ } ++ remove = true; ++ } else if ((((int) this.goalTypes.backingSet) & flags) == 0 && unrolledIter(flags, goal.getPriority(), ctxLockedFlags)) { ++ // entity and block ++ if (goal.canUse()) { ++ unrolledIter2(flags, goal); ++ goal.start(); ++ } ++ if (ctx.wake != null) { ++ ctx.state = false; ++ return true; ++ } ++ remove = true; ++ } else { ++ remove = false; ++ } ++ if (remove) { ++ ctxRunning[ctxRunningLength] = ctxNonRunning[ctxIndex - 1]; ++ ctxRunningLength++; ++ if (ctxIndex != ctxNonRunningLength) { ++ ctxNonRunning[ctxIndex - 1] = ctxNonRunning[ctxNonRunningLength - 1]; ++ } ++ ctxNonRunningLength--; ++ } ++ ctxIndex--; + ctx.state = true; + } + return false; + } ++ ++ private void stateTickRunningAll() { ++ while (ctxIndex != 0) { ++ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; ++ if (goal.isRunning()) { ++ goal.tick(); ++ } ++ ctxIndex--; ++ } ++ } ++ ++ private void stateTickRunningPartial() { ++ while (ctxIndex != 0) { ++ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; ++ if (goal.isRunning() && goal.requiresUpdateEveryTick()) { ++ goal.tick(); ++ } ++ ctxIndex--; ++ } ++ } ++ ++ private void unrolledIter2(int iter, WrappedGoal goal) { ++ if (iter == 0) { ++ return; ++ } ++ int priority = goal.isInterruptable() ? goal.getPriority() : -1; ++ if ((iter & 0b1) != 0) { ++ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[0], goal); ++ if (wrappedGoal1 != null) { ++ wrappedGoal1.stop(); ++ } ++ ctxLockedFlags[0] = priority; ++ } ++ if ((iter & 0b10) != 0) { ++ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[1], goal); ++ if (wrappedGoal1 != null) { ++ wrappedGoal1.stop(); ++ } ++ ctxLockedFlags[1] = priority; ++ } ++ if ((iter & 0b100) != 0) { ++ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[2], goal); ++ if (wrappedGoal1 != null) { ++ wrappedGoal1.stop(); ++ } ++ ctxLockedFlags[2] = priority; ++ } ++ if ((iter & 0b1000) != 0) { ++ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[3], goal); ++ if (wrappedGoal1 != null) { ++ wrappedGoal1.stop(); ++ } ++ ctxLockedFlags[3] = priority; ++ } ++ if ((iter & 0b10000) != 0) { ++ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[4], goal); ++ if (wrappedGoal1 != null) { ++ wrappedGoal1.stop(); ++ } ++ ctxLockedFlags[4] = priority; ++ } ++ } ++ ++ private static boolean unrolledIter(int iter, int priority, int[] arr) { ++ return iter == 0 || (((iter & 0b1) == 0 || priority < arr[0]) ++ && ((iter & 0b10) == 0 || priority < arr[1]) ++ && ((iter & 0b100) == 0 || priority < arr[2]) ++ && ((iter & 0b1000) == 0 || priority < arr[3]) ++ && ((iter & 0b10000) == 0 || priority < arr[4])); ++ } + // Leaf end - Async target finding + public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { -+ if (availableGoalsDirty || this.ctxGoals == null) { -+ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); -+ availableGoalsDirty = false; -+ } -+ ctxState = 0; ++ ctxState = 65536; + } + return; + } @@ -850,18 +1009,14 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); -@@ -116,6 +251,18 @@ public class GoalSelector { +@@ -116,6 +388,14 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { -+ if (availableGoalsDirty || this.ctxGoals == null) { -+ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); -+ availableGoalsDirty = false; -+ } -+ ctxState = tickAllRunning ? 2 : 3; ++ ctxState = tickAllRunning ? 2 + 65536 : 3 + 65536; + } + return; + } @@ -1419,6 +1574,18 @@ index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50 this.player = getServerLevel(this.mob) .getNearestPlayer(this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)), this.mob); // CraftBukkit start +diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java +index 2c2ab6a1df9d3d23773e44ce4041cc1c21b55163..8a184ddedb9dd4e394d23732741ce219f0685669 100644 +--- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java ++++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java +@@ -14,6 +14,7 @@ public class WrappedGoal extends Goal { + } + + public boolean canBeReplacedBy(WrappedGoal other) { ++ // Leaf - Async target finding - inline diff + return this.isInterruptable() && other.getPriority() < this.getPriority(); + } + diff --git a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7abf8559358 100644 --- a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java diff --git a/leaf-server/paper-patches/features/0038-Async-target-finding.patch b/leaf-server/paper-patches/features/0038-Async-target-finding.patch new file mode 100644 index 00000000..3fe32c98 --- /dev/null +++ b/leaf-server/paper-patches/features/0038-Async-target-finding.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Thu, 22 May 2025 20:56:06 +0900 +Subject: [PATCH] Async target finding + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java b/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java +index 4123edddc556c47f3f8d83523c125fd2e46b30e2..10dd3a4af3568d7aafe3a27113a0a8b6a8ac5a19 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java +@@ -4,8 +4,8 @@ import java.util.Collection; + + public final class OptimizedSmallEnumSet> { + +- private final Class enumClass; +- private long backingSet; ++ // private final Class enumClass; // Leaf ++ public long backingSet; // Leaf + + public OptimizedSmallEnumSet(final Class clazz) { + if (clazz == null) { +@@ -14,7 +14,7 @@ public final class OptimizedSmallEnumSet> { + if (!clazz.isEnum()) { + throw new IllegalArgumentException("Class must be enum, not " + clazz.getCanonicalName()); + } +- this.enumClass = clazz; ++ // this.enumClass = clazz; // Leaf + } + + public boolean addUnchecked(final E element) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index 084ed5df..192530ed 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dreeam.leaf.config.modules.async.AsyncTargetFinding; import org.dreeam.leaf.util.queue.SpscIntQueue; +import org.jetbrains.annotations.NotNull; import java.util.OptionalInt; import java.util.concurrent.locks.LockSupport; @@ -17,7 +18,7 @@ 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 IntArrayList submit; private final AsyncGoalThread thread; private final ServerLevel world; private long midTickCount = 0L; @@ -31,7 +32,7 @@ public class AsyncGoalExecutor { } boolean wake(int id) { - Entity entity = this.world.getEntities().get(id); + Entity entity = this.world.getEntity(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } @@ -40,8 +41,12 @@ public class AsyncGoalExecutor { return true; } - public final void submit(int entityId) { - this.submit.add(entityId); + public final void submit(@NotNull Mob mob) { + this.submit.add(mob.getId()); + } + + public final boolean hasTasks(@NotNull Mob mob) { + return mob.targetSelector.ctxState >= 65536 || mob.goalSelector.ctxState >= 65536; } public final void tick() { @@ -87,7 +92,7 @@ public class AsyncGoalExecutor { } private boolean poll(int id) { - Entity entity = this.world.getEntities().get(id); + Entity entity = this.world.getEntity(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java index 37e557ef..976de5f9 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java @@ -11,6 +11,9 @@ public class Waker { public boolean state = true; public final @Nullable Object result() { + if (state) { + return null; + } Object result = this.result; this.result = null; return result; From e635996b35fe6f54c43909e7912a9e207c6bb552 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 23 May 2025 15:58:11 +0900 Subject: [PATCH 78/81] optimize getEntityStatus --- .../0182-optimize-getEntityStatus.patch | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch diff --git a/leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch b/leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch new file mode 100644 index 00000000..7d0a36aa --- /dev/null +++ b/leaf-server/minecraft-patches/features/0182-optimize-getEntityStatus.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 23 May 2025 15:57:42 +0900 +Subject: [PATCH] optimize getEntityStatus + + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +index 7554c109c35397bc1a43dd80e87764fd78645bbf..151476fd036839a416c226599279d0d8bf79717b 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +@@ -93,8 +93,14 @@ public abstract class EntityLookup implements LevelEntityGetter { + if (entity == null) { + return null; + } +- final Visibility visibility = EntityLookup.getEntityStatus(entity); +- return visibility.isAccessible() ? entity : null; ++ // Leaf start ++ final FullChunkStatus entityStatus = ((ChunkSystemEntity) entity).moonrise$getChunkStatus(); ++ return switch (entityStatus) { ++ case INACCESSIBLE -> null; ++ case FULL, BLOCK_TICKING, ENTITY_TICKING -> entity; ++ case null -> null; ++ }; ++ // Leaf end + } + + @Override +@@ -394,7 +400,14 @@ public abstract class EntityLookup implements LevelEntityGetter { + return Visibility.TICKING; + } + final FullChunkStatus entityStatus = ((ChunkSystemEntity)entity).moonrise$getChunkStatus(); +- return Visibility.fromFullChunkStatus(entityStatus == null ? FullChunkStatus.INACCESSIBLE : entityStatus); ++ // Leaf start ++ return switch (entityStatus) { ++ case INACCESSIBLE -> Visibility.HIDDEN; ++ case FULL, BLOCK_TICKING -> Visibility.TRACKED; ++ case ENTITY_TICKING -> Visibility.TICKING; ++ case null -> Visibility.HIDDEN; ++ }; ++ // Leaf end + } + + protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) { +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index df23d80d6b18e900414aa02e5c1812f0a10f0fb7..9f581d5bdc3f658694bbd8c80abbce4e27e568d3 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -381,6 +381,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // Paper end + // Paper start - rewrite chunk system + private final boolean isHardColliding = this.moonrise$isHardCollidingUncached(); ++ @org.jetbrains.annotations.Nullable // Leaf + private net.minecraft.server.level.FullChunkStatus chunkStatus; + private ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData chunkData; + private int sectionX = Integer.MIN_VALUE; +@@ -394,6 +395,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + @Override ++ @org.jetbrains.annotations.Nullable // Leaf + public final net.minecraft.server.level.FullChunkStatus moonrise$getChunkStatus() { + return this.chunkStatus; + } From 577ed2650eb2ca36af5c06a0a3f6c58c0965869f Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 23 May 2025 15:16:39 +0200 Subject: [PATCH 79/81] add ignoreSpawnRules to SpawnerSettings --- .../0133-Spawner-Configurations.patch | 44 +++++++++++++------ .../modules/gameplay/SpawnerSettings.java | 11 ++++- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch index e28ecea2..540bfe40 100644 --- a/leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch +++ b/leaf-server/minecraft-patches/features/0133-Spawner-Configurations.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Spawner Configurations diff --git a/net/minecraft/world/level/BaseSpawner.java b/net/minecraft/world/level/BaseSpawner.java -index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab7437984d02 100644 +index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..fd1c151958ec2535e56d2d04938803d586eeca8e 100644 --- a/net/minecraft/world/level/BaseSpawner.java +++ b/net/minecraft/world/level/BaseSpawner.java @@ -53,6 +53,12 @@ public abstract class BaseSpawner { @@ -58,7 +58,7 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 if (this.isNearPlayer(serverLevel, pos)) { if (this.spawnDelay < -tickDelay) { // Paper - Configurable mob spawner tick rate this.delay(serverLevel, pos); -@@ -107,18 +136,43 @@ public abstract class BaseSpawner { +@@ -107,18 +136,49 @@ public abstract class BaseSpawner { double d = size >= 1 ? list.getDouble(0) : pos.getX() + (random.nextDouble() - random.nextDouble()) * this.spawnRange + 0.5; double d1 = size >= 2 ? list.getDouble(1) : pos.getY() + random.nextInt(3) - 1; double d2 = size >= 3 ? list.getDouble(2) : pos.getZ() + (random.nextDouble() - random.nextDouble()) * this.spawnRange + 0.5; @@ -69,8 +69,11 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 + boolean skipBlockChecks = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && + !org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.spawnerBlockChecks; + if (skipBlockChecks || serverLevel.noCollision(optional.get().getSpawnAABB(d, d1, d2))) { - BlockPos blockPos = BlockPos.containing(d, d1, d2); ++ // 'skipBlockChecks' is true if SpawnerSettings.spawnerBlockChecks is false. ++ // It means we skip physical block checks like collision and custom rule isValidPosition. + + BlockPos blockPos = BlockPos.containing(d, d1, d2); ++ + // Add light level check if enabled + if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && + org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.lightLevelCheck) { @@ -87,8 +90,9 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 + continue; + } + -+ // Handle spawn rules checks -+ boolean skipSpawnRules = false; ++ // Determine if mob-specific spawn rules (like block types, biome requirements) should be skipped ++ boolean skipMobSpecificRules = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && ++ org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.ignoreSpawnRules; + // Leaf end - Spawner Configurations if (nextSpawnData.getCustomSpawnRules().isPresent()) { if (!optional.get().getCategory().isFriendly() && serverLevel.getDifficulty() == Difficulty.PEACEFUL) { @@ -97,15 +101,17 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 SpawnData.CustomSpawnRules customSpawnRules = nextSpawnData.getCustomSpawnRules().get(); - if (!customSpawnRules.isValidPosition(blockPos, serverLevel)) { ++ // customSpawnRules.isValidPosition is controlled by spawnerBlockChecks (via !skipBlockChecks) + if (!skipBlockChecks && !customSpawnRules.isValidPosition(blockPos, serverLevel)) { // Leaf - Spawner Configurations continue; } - } else if (!SpawnPlacements.checkSpawnRules(optional.get(), serverLevel, EntitySpawnReason.SPAWNER, blockPos, serverLevel.getRandom())) { -+ } else if (!skipBlockChecks && !SpawnPlacements.checkSpawnRules(optional.get(), serverLevel, EntitySpawnReason.SPAWNER, blockPos, serverLevel.getRandom())) { // Leaf - Spawner Configurations ++ } else if (!skipMobSpecificRules && !SpawnPlacements.checkSpawnRules(optional.get(), serverLevel, EntitySpawnReason.SPAWNER, blockPos, serverLevel.getRandom())) { ++ // If not skipping mob-specific rules AND standard spawn rules fail, continue. continue; } -@@ -146,6 +200,7 @@ public abstract class BaseSpawner { +@@ -146,6 +206,7 @@ public abstract class BaseSpawner { return; } @@ -113,7 +119,7 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 int size1 = serverLevel.getEntities( EntityTypeTest.forExactClass(entity.getClass()), new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1).inflate(this.spawnRange), -@@ -156,12 +211,16 @@ public abstract class BaseSpawner { +@@ -156,12 +217,28 @@ public abstract class BaseSpawner { this.delay(serverLevel, pos); return; } @@ -125,14 +131,26 @@ index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab74 - if (nextSpawnData.getCustomSpawnRules().isEmpty() && !mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER) - || !mob.checkSpawnObstruction(serverLevel)) { + // Leaf start - Spawner Configurations -+ // Skip spawn rule and obstruction checks if block checks are disabled -+ if (!skipBlockChecks && (nextSpawnData.getCustomSpawnRules().isEmpty() && !mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER) -+ || !mob.checkSpawnObstruction(serverLevel))) { -+ // Leaf end - Spawner Configurations ++ // mob.checkSpawnRules is controlled by ignoreSpawnRules (via !skipMobSpecificRules) ++ // mob.checkSpawnObstruction is controlled by spawnerBlockChecks (via !skipBlockChecks) ++ ++ boolean mobSpecificRulesFailed = false; ++ if (nextSpawnData.getCustomSpawnRules().isEmpty() && !skipMobSpecificRules) { ++ if (!mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER)) { ++ mobSpecificRulesFailed = true; ++ } ++ } ++ ++ boolean obstructionFailed = false; ++ if (!skipBlockChecks && !mob.checkSpawnObstruction(serverLevel)) { // If not skipping physical checks and obstruction fails ++ obstructionFailed = true; ++ } ++ ++ if (mobSpecificRulesFailed || obstructionFailed) { continue; } -@@ -249,6 +308,13 @@ public abstract class BaseSpawner { +@@ -249,6 +326,13 @@ public abstract class BaseSpawner { this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData()); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java index 31e81d18..7a9c0aa9 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java @@ -18,6 +18,7 @@ public class SpawnerSettings extends ConfigModules { public static boolean checkForNearbyPlayers = true; public static boolean spawnerBlockChecks = false; public static boolean waterPreventSpawnCheck = false; + public static boolean ignoreSpawnRules = false; public static int minSpawnDelay = 200; public static int maxSpawnDelay = 800; @@ -60,8 +61,8 @@ public class SpawnerSettings extends ConfigModules { spawnerBlockChecks = config.getBoolean(getBasePath() + ".checks.spawner-block-checks", spawnerBlockChecks, config.pickStringRegionBased( - "Check if there are blocks blocking the spawner to spawn the mob", - "检查是否有方块阻挡刷怪笼生成怪物" + "Check if there are physical blocks obstructing the spawn location, or if custom spawn rules (isValidPosition) fail due to block conditions.", + "检查是否有物理方块阻挡生成位置, 或自定义生成规则(isValidPosition)因方块条件失败." )); waterPreventSpawnCheck = config.getBoolean(getBasePath() + ".checks.water-prevent-spawn-check", waterPreventSpawnCheck, @@ -70,6 +71,12 @@ public class SpawnerSettings extends ConfigModules { "检查周围是否有水阻止生成" )); + ignoreSpawnRules = config.getBoolean(getBasePath() + ".checks.ignore-spawn-rules", ignoreSpawnRules, + config.pickStringRegionBased( + "Ignore mob-specific spawn rules, like animals needing grass or specific biomes/blocks (does not affect light level or physical obstruction checks).", + "忽略特定于生物的生成规则, 例如动物需要草方块或特定的生物群系/方块 (不影响光照等级或物理障碍物检查)." + )); + // Delay settings minSpawnDelay = config.getInt(getBasePath() + ".min-spawn-delay", minSpawnDelay, From 2deb6989f26b155ffbe6cce820645b36c92e4e3a Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Fri, 23 May 2025 23:54:41 +0900 Subject: [PATCH 80/81] Revert "optimize GoalSelector update order" This reverts commit 1c6aa6076986ae5bb5235653a80cb3e57af96086. --- .../features/0153-Async-target-finding.patch | 401 +++++------------- .../features/0038-Async-target-finding.patch | 30 -- .../leaf/async/ai/AsyncGoalExecutor.java | 15 +- .../java/org/dreeam/leaf/async/ai/Waker.java | 3 - 4 files changed, 122 insertions(+), 327 deletions(-) delete mode 100644 leaf-server/paper-patches/features/0038-Async-target-finding.patch diff --git a/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch index eda41928..fd386af4 100644 --- a/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0153-Async-target-finding.patch @@ -237,7 +237,7 @@ index 9af7dafe03812d96aa477584d4147a68c240ab21..e6fd46b8148e050c4807abf6c8a03e47 // Paper start - log detailed entity tick information diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..44d167e9c92260e7e5a134d7dae310b90d5cd7ff 100644 +index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..88809afe30bb970a7de8bdfd269268800516c426 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -144,6 +144,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -253,17 +253,12 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..44d167e9c92260e7e5a134d7dae310b9 protected Mob(EntityType entityType, Level level) { super(entityType, level); -@@ -225,12 +231,27 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -225,12 +231,22 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab } // Paper end - Skip AI during inactive ticks for non-aware mobs boolean isThrottled = org.dreeam.leaf.config.modules.opt.ThrottleInactiveGoalSelectorTick.enabled && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking + // Leaf start - Async target finding -+ boolean hasTasks; -+ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ hasTasks = ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this); -+ } else { -+ hasTasks = true; -+ } ++ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; + this.tickingTarget = false; if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking this.goalSelector.tick(); @@ -273,25 +268,20 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..44d167e9c92260e7e5a134d7dae310b9 this.targetSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!hasTasks && ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this)) { -+ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this); ++ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { ++ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } + // Leaf end - Async target finding } // Paper end -@@ -914,17 +935,34 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -914,17 +930,29 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab // Paper end - Allow nerfed mobs to jump and float this.sensing.tick(); int i = this.tickCount + this.getId(); + // Leaf start - Async target finding -+ boolean hasTasks; -+ if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ hasTasks = ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this); -+ } else { -+ hasTasks = true; -+ } ++ boolean running = this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1; if (i % 2 != 0 && this.tickCount > 1) { + this.tickingTarget = true; if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking @@ -308,8 +298,8 @@ index 05d5cde42b7011091ef4ee874c0d9d5586ae3f10..44d167e9c92260e7e5a134d7dae310b9 this.goalSelector.tick(); } + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ if (!hasTasks && ((ServerLevel) this.level()).asyncGoalExecutor.hasTasks(this)) { -+ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this); ++ if (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { ++ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); + } + } + // Leaf end - Async target finding @@ -678,34 +668,17 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 return false; } else { this.parent = animal; -diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java -index 5f5bf0e710ecff09a571091e5a923332be70cb74..487da7485bb957ef6516c2950035ca304b84edd7 100644 ---- a/net/minecraft/world/entity/ai/goal/Goal.java -+++ b/net/minecraft/world/entity/ai/goal/Goal.java -@@ -100,6 +100,7 @@ public abstract class Goal { - // Paper end - Mob goal api - - public static enum Flag { -+ // Leaf - Async target finding - inline diff - UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR - MOVE, - LOOK, diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index e82e32407cec6109b9c3b0106295217f4a3f4aa2..4811081a1ec32432958473f8f58b1a1cde76a2cd 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb506310de8461 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -26,13 +26,28 @@ public class GoalSelector { +@@ -26,13 +26,23 @@ public class GoalSelector { private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 + // Leaf start - Async target finding -+ private boolean ctxDirty = true; -+ private WrappedGoal[] ctxGoals = {}; -+ private int[] ctxRunning = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; -+ private int[] ctxNonRunning = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; -+ private int[] ctxLockedFlags = it.unimi.dsi.fastutil.ints.IntArrays.EMPTY_ARRAY; -+ private int ctxRunningLength = 0; -+ private int ctxNonRunningLength = 0; ++ private boolean availableGoalsDirty = true; ++ private WrappedGoal @org.jetbrains.annotations.Nullable[] ctxGoals = null; + private int ctxIndex = 0; + public int ctxState = -1; + public final org.dreeam.leaf.async.ai.Waker ctx = new org.dreeam.leaf.async.ai.Waker(); @@ -713,21 +686,21 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..4811081a1ec32432958473f8f58b1a1c + public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); -+ ctxDirty = true; // Leaf - Async target finding ++ availableGoalsDirty = true; // Leaf - Async target finding } @VisibleForTesting public void removeAllGoals(Predicate filter) { this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); -+ ctxDirty = true; // Leaf - Async target finding ++ availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - EAR 2 -@@ -63,18 +78,19 @@ public class GoalSelector { +@@ -63,18 +73,19 @@ public class GoalSelector { } this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); -+ ctxDirty = true; // Leaf - Async target finding ++ availableGoalsDirty = true; // Leaf - Async target finding } // Paper start - Perf: optimize goal types @@ -745,262 +718,130 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..4811081a1ec32432958473f8f58b1a1c flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); // Paper end - Perf: optimize goal types if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { -@@ -85,7 +101,263 @@ public class GoalSelector { +@@ -85,7 +96,131 @@ public class GoalSelector { return true; } + // Leaf start - Async target finding + public final boolean poll() { -+ if (ctx.wake != null) { ++ if (this.ctxGoals == null || ctx.wake != null) { + return false; + } -+ if (ctxState >= 65536) { -+ ctxState -= 65536; -+ ctxIndex = ctxRunningLength; -+ ctx.state = true; -+ if (ctxDirty) { -+ stateInit(); -+ } -+ } + if (ctxState == 0) { -+ if (stateCanContinueToUseStop()) return true; -+ ctxIndex = ctxNonRunningLength; ++ while (ctxIndex < this.ctxGoals.length) { ++ WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ // entity tempt ++ if (goal.isRunning() && (goalContainsAnyFlags(goal, this.goalTypes) || !goal.canContinueToUse())) { ++ ctx.state = false; ++ if (ctx.wake != null) { ++ return true; ++ } ++ goal.stop(); ++ } ++ ++ ctxIndex++; ++ ctx.state = true; ++ } ++ ++ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); ++ ++ ctxIndex = 0; + ctx.state = true; + ctxState = 1; + } + if (ctxState == 1) { -+ if (stateCanUseStart()) return true; -+ ctxIndex = ctxRunningLength; ++ while (ctxIndex < this.ctxGoals.length) { ++ WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ var flags = goal.getFlags(); ++ // entity and block ++ if (!goal.isRunning() && !flags.hasCommonElements(this.goalTypes)) { ++ // inline ++ boolean result = true; ++ long flagIterator1 = flags.getBackingSet(); ++ int wrappedGoalSize1 = flags.size(); ++ for (int i1 = 0; i1 < wrappedGoalSize1; ++i1) { ++ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator1)]; ++ flagIterator1 ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator1); ++ if (!this.lockedFlags.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { ++ result = false; ++ break; ++ } ++ } ++ if (result) { ++ if (goal.canUse()) { ++ long flagIterator = flags.getBackingSet(); ++ int wrappedGoalSize = flags.size(); ++ for (int i = 0; i < wrappedGoalSize; ++i) { ++ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); ++ WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); ++ wrappedGoal1.stop(); ++ this.lockedFlags.put(flag, goal); ++ } ++ ++ goal.start(); ++ } ++ ctx.state = false; ++ if (ctx.wake != null) { ++ return true; ++ } ++ } ++ } ++ // entity alert other ++ if (!ctx.state) { ++ switch (goal.getGoal()) { ++ case net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal t -> t.poll(); ++ case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); ++ default -> {} ++ } ++ } ++ ctxIndex++; ++ ctx.state = true; ++ } ++ ++ ctxIndex = 0; + ctx.state = true; + ctxState = 2; + } + if (ctxState == 2) { -+ stateTickRunningAll(); ++ while (ctxIndex < this.ctxGoals.length) { ++ WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ if (goal.isRunning()) { ++ goal.tick(); ++ } ++ ctxIndex++; ++ } ++ + ctxState = -1; ++ ctxIndex = 0; ++ ctx.state = true; + } + if (ctxState == 3) { -+ stateTickRunningPartial(); ++ while (ctxIndex < this.ctxGoals.length) { ++ WrappedGoal goal = this.ctxGoals[ctxIndex]; ++ if (goal.isRunning() && goal.requiresUpdateEveryTick()) { ++ goal.tick(); ++ } ++ ctxIndex++; ++ } ++ + ctxState = -1; -+ } -+ return false; -+ } -+ -+ private void stateInit() { -+ ctxDirty = false; -+ { -+ if (availableGoals.size() > ctxGoals.length) { -+ ctxGoals = new WrappedGoal[availableGoals.size()]; -+ } -+ int i = 0; -+ for (WrappedGoal goal : this.availableGoals) { -+ ctxGoals[i] = goal; -+ i += 1; -+ } -+ for (; i < ctxGoals.length; i++) { -+ ctxGoals[i] = NO_GOAL; -+ } -+ } -+ if (ctxGoals.length > ctxRunning.length) { -+ ctxRunning = new int[ctxGoals.length]; -+ } -+ if (ctxGoals.length > ctxNonRunning.length) { -+ ctxNonRunning = new int[ctxGoals.length]; -+ } -+ ctxRunningLength = 0; -+ ctxNonRunningLength = 0; -+ for (int i = 0; i < ctxGoals.length; i++) { -+ final WrappedGoal goal = ctxGoals[i]; -+ if (goal.isRunning()) { -+ ctxRunning[ctxRunningLength] = i; -+ ctxRunningLength += 1; -+ } else { -+ ctxNonRunning[ctxNonRunningLength] = i; -+ ctxNonRunningLength += 1; -+ } -+ } -+ it.unimi.dsi.fastutil.ints.IntArrays.quickSort(ctxRunning, 0, ctxRunningLength, (i, j) -> Integer.compare(ctxGoals[j].getPriority(), ctxGoals[i].getPriority())); -+ it.unimi.dsi.fastutil.ints.IntArrays.quickSort(ctxNonRunning, 0, ctxNonRunningLength, (i, j) -> Integer.compare(ctxGoals[j].getPriority(), ctxGoals[i].getPriority())); -+ -+ if (ctxLockedFlags.length == 0) { -+ ctxLockedFlags = new int[GOAL_FLAG_VALUES.length]; -+ } -+ for (int i = 0; i < GOAL_FLAG_VALUES.length; i++) { -+ final WrappedGoal goal = lockedFlags.get(GOAL_FLAG_VALUES[i]); -+ if (goal != null) { -+ if (goal.isInterruptable()) { -+ ctxLockedFlags[i] = goal.getPriority(); -+ } else { -+ ctxLockedFlags[i] = -1; -+ } -+ } else { -+ ctxLockedFlags[i] = Integer.MAX_VALUE; -+ } -+ } -+ } -+ -+ -+ private boolean stateCanContinueToUseStop() { -+ while (ctxIndex != 0) { -+ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; -+ final int flags = (int) goal.getFlags().backingSet; -+ boolean remove; -+ // entity TemptGoal -+ if ((((int) this.goalTypes.backingSet) & flags) != 0 || !goal.isRunning()) { -+ goal.stop(); -+ remove = true; -+ } else if (!goal.canContinueToUse()) { -+ if (ctx.wake != null) { -+ ctx.state = false; -+ return true; -+ } -+ goal.stop(); -+ remove = true; -+ } else { -+ remove = false; -+ } -+ -+ if (remove) { -+ ctxNonRunning[ctxNonRunningLength] = ctxRunning[ctxIndex - 1]; -+ ctxNonRunningLength++; -+ if (ctxIndex != ctxRunningLength) { -+ ctxRunning[ctxIndex - 1] = ctxRunning[ctxRunningLength - 1]; -+ } -+ ctxRunningLength--; -+ } -+ ctxIndex--; -+ ctx.state = true; -+ } -+ -+ for (int i = 0; i < GOAL_FLAG_VALUES.length; i++) { -+ final WrappedGoal goal = lockedFlags.get(GOAL_FLAG_VALUES[i]); -+ if (goal != null && !goal.isRunning()) { -+ lockedFlags.remove(GOAL_FLAG_VALUES[i]); -+ ctxLockedFlags[i] = Integer.MAX_VALUE; -+ } -+ } -+ return false; -+ } -+ -+ private boolean stateCanUseStart() { -+ while (ctxIndex != 0) { -+ boolean remove; -+ final WrappedGoal goal = ctxGoals[ctxNonRunning[ctxIndex - 1]]; -+ final int flags = (int) goal.getFlags().backingSet; -+ if (goal.isRunning()) { -+ // entity alertOther -+ if (!ctx.state) { -+ switch (goal.getGoal()) { -+ case net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal t -> t.poll(); -+ case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); -+ default -> { -+ } -+ } -+ } -+ remove = true; -+ } else if ((((int) this.goalTypes.backingSet) & flags) == 0 && unrolledIter(flags, goal.getPriority(), ctxLockedFlags)) { -+ // entity and block -+ if (goal.canUse()) { -+ unrolledIter2(flags, goal); -+ goal.start(); -+ } -+ if (ctx.wake != null) { -+ ctx.state = false; -+ return true; -+ } -+ remove = true; -+ } else { -+ remove = false; -+ } -+ if (remove) { -+ ctxRunning[ctxRunningLength] = ctxNonRunning[ctxIndex - 1]; -+ ctxRunningLength++; -+ if (ctxIndex != ctxNonRunningLength) { -+ ctxNonRunning[ctxIndex - 1] = ctxNonRunning[ctxNonRunningLength - 1]; -+ } -+ ctxNonRunningLength--; -+ } -+ ctxIndex--; ++ ctxIndex = 0; + ctx.state = true; + } + return false; + } -+ -+ private void stateTickRunningAll() { -+ while (ctxIndex != 0) { -+ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; -+ if (goal.isRunning()) { -+ goal.tick(); -+ } -+ ctxIndex--; -+ } -+ } -+ -+ private void stateTickRunningPartial() { -+ while (ctxIndex != 0) { -+ final WrappedGoal goal = ctxGoals[ctxRunning[ctxIndex - 1]]; -+ if (goal.isRunning() && goal.requiresUpdateEveryTick()) { -+ goal.tick(); -+ } -+ ctxIndex--; -+ } -+ } -+ -+ private void unrolledIter2(int iter, WrappedGoal goal) { -+ if (iter == 0) { -+ return; -+ } -+ int priority = goal.isInterruptable() ? goal.getPriority() : -1; -+ if ((iter & 0b1) != 0) { -+ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[0], goal); -+ if (wrappedGoal1 != null) { -+ wrappedGoal1.stop(); -+ } -+ ctxLockedFlags[0] = priority; -+ } -+ if ((iter & 0b10) != 0) { -+ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[1], goal); -+ if (wrappedGoal1 != null) { -+ wrappedGoal1.stop(); -+ } -+ ctxLockedFlags[1] = priority; -+ } -+ if ((iter & 0b100) != 0) { -+ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[2], goal); -+ if (wrappedGoal1 != null) { -+ wrappedGoal1.stop(); -+ } -+ ctxLockedFlags[2] = priority; -+ } -+ if ((iter & 0b1000) != 0) { -+ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[3], goal); -+ if (wrappedGoal1 != null) { -+ wrappedGoal1.stop(); -+ } -+ ctxLockedFlags[3] = priority; -+ } -+ if ((iter & 0b10000) != 0) { -+ WrappedGoal wrappedGoal1 = lockedFlags.put(GOAL_FLAG_VALUES[4], goal); -+ if (wrappedGoal1 != null) { -+ wrappedGoal1.stop(); -+ } -+ ctxLockedFlags[4] = priority; -+ } -+ } -+ -+ private static boolean unrolledIter(int iter, int priority, int[] arr) { -+ return iter == 0 || (((iter & 0b1) == 0 || priority < arr[0]) -+ && ((iter & 0b10) == 0 || priority < arr[1]) -+ && ((iter & 0b100) == 0 || priority < arr[2]) -+ && ((iter & 0b1000) == 0 || priority < arr[3]) -+ && ((iter & 0b10000) == 0 || priority < arr[4])); -+ } + // Leaf end - Async target finding + public void tick() { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { -+ ctxState = 65536; ++ if (availableGoalsDirty || this.ctxGoals == null) { ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ availableGoalsDirty = false; ++ } ++ ctxState = 0; + } + return; + } @@ -1009,14 +850,18 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..4811081a1ec32432958473f8f58b1a1c for (WrappedGoal wrappedGoal : this.availableGoals) { if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); -@@ -116,6 +388,14 @@ public class GoalSelector { +@@ -116,6 +251,18 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { + if (ctxState == -1) { -+ ctxState = tickAllRunning ? 2 + 65536 : 3 + 65536; ++ if (availableGoalsDirty || this.ctxGoals == null) { ++ this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); ++ availableGoalsDirty = false; ++ } ++ ctxState = tickAllRunning ? 2 : 3; + } + return; + } @@ -1574,18 +1419,6 @@ index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50 this.player = getServerLevel(this.mob) .getNearestPlayer(this.targetingConditions.range(this.mob.getAttributeValue(Attributes.TEMPT_RANGE)), this.mob); // CraftBukkit start -diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java -index 2c2ab6a1df9d3d23773e44ce4041cc1c21b55163..8a184ddedb9dd4e394d23732741ce219f0685669 100644 ---- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java -+++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java -@@ -14,6 +14,7 @@ public class WrappedGoal extends Goal { - } - - public boolean canBeReplacedBy(WrappedGoal other) { -+ // Leaf - Async target finding - inline diff - return this.isInterruptable() && other.getPriority() < this.getPriority(); - } - diff --git a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7abf8559358 100644 --- a/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java diff --git a/leaf-server/paper-patches/features/0038-Async-target-finding.patch b/leaf-server/paper-patches/features/0038-Async-target-finding.patch deleted file mode 100644 index 3fe32c98..00000000 --- a/leaf-server/paper-patches/features/0038-Async-target-finding.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Thu, 22 May 2025 20:56:06 +0900 -Subject: [PATCH] Async target finding - - -diff --git a/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java b/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java -index 4123edddc556c47f3f8d83523c125fd2e46b30e2..10dd3a4af3568d7aafe3a27113a0a8b6a8ac5a19 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java -+++ b/src/main/java/ca/spottedleaf/moonrise/common/set/OptimizedSmallEnumSet.java -@@ -4,8 +4,8 @@ import java.util.Collection; - - public final class OptimizedSmallEnumSet> { - -- private final Class enumClass; -- private long backingSet; -+ // private final Class enumClass; // Leaf -+ public long backingSet; // Leaf - - public OptimizedSmallEnumSet(final Class clazz) { - if (clazz == null) { -@@ -14,7 +14,7 @@ public final class OptimizedSmallEnumSet> { - if (!clazz.isEnum()) { - throw new IllegalArgumentException("Class must be enum, not " + clazz.getCanonicalName()); - } -- this.enumClass = clazz; -+ // this.enumClass = clazz; // Leaf - } - - public boolean addUnchecked(final E element) { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java index 192530ed..084ed5df 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/AsyncGoalExecutor.java @@ -8,7 +8,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dreeam.leaf.config.modules.async.AsyncTargetFinding; import org.dreeam.leaf.util.queue.SpscIntQueue; -import org.jetbrains.annotations.NotNull; import java.util.OptionalInt; import java.util.concurrent.locks.LockSupport; @@ -18,7 +17,7 @@ public class AsyncGoalExecutor { protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); protected final SpscIntQueue queue; protected final SpscIntQueue wake; - private final IntArrayList submit; + protected final IntArrayList submit; private final AsyncGoalThread thread; private final ServerLevel world; private long midTickCount = 0L; @@ -32,7 +31,7 @@ public class AsyncGoalExecutor { } boolean wake(int id) { - Entity entity = this.world.getEntity(id); + Entity entity = this.world.getEntities().get(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } @@ -41,12 +40,8 @@ public class AsyncGoalExecutor { return true; } - public final void submit(@NotNull Mob mob) { - this.submit.add(mob.getId()); - } - - public final boolean hasTasks(@NotNull Mob mob) { - return mob.targetSelector.ctxState >= 65536 || mob.goalSelector.ctxState >= 65536; + public final void submit(int entityId) { + this.submit.add(entityId); } public final void tick() { @@ -92,7 +87,7 @@ public class AsyncGoalExecutor { } private boolean poll(int id) { - Entity entity = this.world.getEntity(id); + Entity entity = this.world.getEntities().get(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { return false; } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java index 976de5f9..37e557ef 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ai/Waker.java @@ -11,9 +11,6 @@ public class Waker { public boolean state = true; public final @Nullable Object result() { - if (state) { - return null; - } Object result = this.result; this.result = null; return result; From a022d84c5b5b52f7e8a62f6bff774d0c23176ed5 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sat, 24 May 2025 11:28:18 +0800 Subject: [PATCH 81/81] Keep Timings class to fix plugin compatibility --- .../features/0005-Remove-Timings.patch | 182 +++++++----------- 1 file changed, 72 insertions(+), 110 deletions(-) diff --git a/leaf-api/paper-patches/features/0005-Remove-Timings.patch b/leaf-api/paper-patches/features/0005-Remove-Timings.patch index b03685a1..d4d89639 100644 --- a/leaf-api/paper-patches/features/0005-Remove-Timings.patch +++ b/leaf-api/paper-patches/features/0005-Remove-Timings.patch @@ -1277,60 +1277,39 @@ index df142a89b8c43acb81eb383eac0ef048a1f49a6e..00000000000000000000000000000000 - } -} diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java -deleted file mode 100644 -index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..0000000000000000000000000000000000000000 +index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..908b610bfdfe911675748211b74b809d9e0af3e5 100644 --- a/src/main/java/co/aikar/timings/Timings.java -+++ /dev/null -@@ -1,325 +0,0 @@ --/* -- * This file is licensed under the MIT License (MIT). -- * -- * Copyright (c) 2014 Daniel Ennis -- * -- * Permission is hereby granted, free of charge, to any person obtaining a copy -- * of this software and associated documentation files (the "Software"), to deal -- * in the Software without restriction, including without limitation the rights -- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -- * copies of the Software, and to permit persons to whom the Software is -- * furnished to do so, subject to the following conditions: -- * -- * The above copyright notice and this permission notice shall be included in -- * all copies or substantial portions of the Software. -- * -- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -- * THE SOFTWARE. -- */ --package co.aikar.timings; -- ++++ b/src/main/java/co/aikar/timings/Timings.java +@@ -23,303 +23,48 @@ + */ + package co.aikar.timings; + -import com.google.common.base.Preconditions; -import com.google.common.collect.EvictingQueue; -import com.google.common.collect.Lists; --import net.kyori.adventure.text.Component; + import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import org.bukkit.Bukkit; --import org.bukkit.command.CommandSender; + import org.bukkit.command.CommandSender; -import org.bukkit.plugin.Plugin; -- + -import java.util.List; -import java.util.Queue; -import java.util.logging.Level; -import org.jetbrains.annotations.NotNull; --import org.jetbrains.annotations.Nullable; -- --/** -- * @deprecated Timings will be removed in the future -- */ --@Deprecated(forRemoval = true) + import org.jetbrains.annotations.Nullable; + ++// Leaf start - Remove Timings + /** + * @deprecated Timings will be removed in the future ++ * Keep this class for plugin compatibility (detect usage) + */ + @Deprecated(forRemoval = true) -@SuppressWarnings({"UnusedDeclaration", "WeakerAccess", "SameParameterValue"}) --public final class Timings { -- + public final class Timings { + - final static List requestingReport = Lists.newArrayList(); - private static final int MAX_HISTORY_FRAMES = 12; - public static final Timing NULL_HANDLER = new NullTimingHandler(); @@ -1340,8 +1319,8 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - private static int historyLength = -1; - private static boolean warnedAboutDeprecationOnEnable; - -- private Timings() {} -- + private Timings() {} + - /** - * Returns a Timing for a plugin corresponding to a name. - * @@ -1417,9 +1396,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @return Enabled or not - */ -- public static boolean isTimingsEnabled() { + public static boolean isTimingsEnabled() { - return timingsEnabled; -- } ++ return false; + } - - /** - *

Sets whether or not the Spigot Timings system should be enabled

@@ -1428,14 +1408,14 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @param enabled Should timings be reported - */ -- public static void setTimingsEnabled(boolean enabled) { + public static void setTimingsEnabled(boolean enabled) { - if (enabled && !warnedAboutDeprecationOnEnable) { - Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); - warnedAboutDeprecationOnEnable = true; - } -- } + } - -- public static Component deprecationMessage() { + public static Component deprecationMessage() { - return Component.text() - .color(TextColor.color(0xffc93a)) - .append(Component.text("[!] The timings profiler is in no-op mode and will be fully removed in a later update.")) @@ -1450,7 +1430,8 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - .clickEvent(ClickEvent.openUrl("https://github.com/PaperMC/Paper/discussions/10565"))) - ) - .build(); -- } ++ return Component.empty(); + } - - /** - *

Sets whether or not the Timings should monitor at Verbose level.

@@ -1459,9 +1440,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @return Enabled or not - */ -- public static boolean isVerboseTimingsEnabled() { + public static boolean isVerboseTimingsEnabled() { - return verboseEnabled; -- } ++ return false; + } - - /** - *

Sets whether or not the Timings should monitor at Verbose level.

@@ -1471,10 +1453,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @param enabled Should high-frequency timings be reported - */ -- public static void setVerboseTimingsEnabled(boolean enabled) { + public static void setVerboseTimingsEnabled(boolean enabled) { - verboseEnabled = enabled; - TimingsManager.needsRecheckEnabled = true; -- } + } - - /** - *

Gets the interval between Timing History report generation.

@@ -1483,9 +1465,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @return Interval in ticks - */ -- public static int getHistoryInterval() { + public static int getHistoryInterval() { - return historyInterval; -- } ++ return 0; + } - - /** - *

Sets the interval between Timing History report generations.

@@ -1497,13 +1480,13 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @param interval Interval in ticks - */ -- public static void setHistoryInterval(int interval) { + public static void setHistoryInterval(int interval) { - historyInterval = Math.max(20*60, interval); - // Recheck the history length with the new Interval - if (historyLength != -1) { - setHistoryLength(historyLength); - } -- } + } - - /** - * Gets how long in ticks Timings history is kept for the server. @@ -1512,9 +1495,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @return Duration in Ticks - */ -- public static int getHistoryLength() { + public static int getHistoryLength() { - return historyLength; -- } ++ return 0; + } - - /** - * Sets how long Timing History reports are kept for the server. @@ -1527,7 +1511,7 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * - * @param length Duration in ticks - */ -- public static void setHistoryLength(int length) { + public static void setHistoryLength(int length) { - // Cap at 12 History Frames, 1 hour at 5 minute frames. - int maxLength = historyInterval * MAX_HISTORY_FRAMES; - // For special cases of servers with special permission to bypass the max. @@ -1544,14 +1528,14 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - } - TimingsManager.HISTORY = EvictingQueue.create(frames); - TimingsManager.HISTORY.addAll(oldQueue); -- } + } - - /** - * Resets all Timing Data - */ -- public static void reset() { + public static void reset() { - TimingsManager.reset(); -- } + } - - /** - * Generates a report and sends it to the specified command sender. @@ -1559,7 +1543,7 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - * If sender is null, ConsoleCommandSender will be used. - * @param sender The sender to send to, or null to use the ConsoleCommandSender - */ -- public static void generateReport(@Nullable CommandSender sender) { + public static void generateReport(@Nullable CommandSender sender) { - if (sender == null) { - sender = Bukkit.getConsoleSender(); - } @@ -1604,9 +1588,10 @@ index 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..00000000000000000000000000000000 - @NotNull - static TimingHandler ofSafe(@Nullable String groupName, @NotNull String name, @Nullable Timing groupHandler) { - return TimingsManager.getHandler(groupName, name, groupHandler); -- } --} -- + } ++ // Leaf end - Remove Timings + } + diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java deleted file mode 100644 index b83e5ff7ada8771fdf27ba9807c77ba6a4ce12da..0000000000000000000000000000000000000000 @@ -2834,37 +2819,6 @@ index 3e61a926620a67daec3af54b72a1b911eaef2ed4..00000000000000000000000000000000 - return new MRUMapCache(map); - } -} -diff --git a/src/main/java/org/bukkit/command/BufferedCommandSender.java b/src/main/java/org/bukkit/command/BufferedCommandSender.java -deleted file mode 100644 -index 45ed63797b13e114bf3795c80a6c3967d8eb2351..0000000000000000000000000000000000000000 ---- a/src/main/java/org/bukkit/command/BufferedCommandSender.java -+++ /dev/null -@@ -1,25 +0,0 @@ --package org.bukkit.command; -- --import org.jetbrains.annotations.NotNull; -- --/** -- * @deprecated Timings will be removed in the future -- */ --@Deprecated(forRemoval = true) --public class BufferedCommandSender implements MessageCommandSender { -- private final StringBuffer buffer = new StringBuffer(); -- @Override -- public void sendMessage(@NotNull String message) { -- buffer.append(message); -- buffer.append("\n"); -- } -- -- @NotNull -- public String getBuffer() { -- return buffer.toString(); -- } -- -- public void reset() { -- this.buffer.setLength(0); -- } --} diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java index 71eb845a4d3b8b6ec3b816a0f20ec807e0f9a86d..a43419c23aa0f6fd809caf5a841cb138f350b7ba 100644 --- a/src/main/java/org/bukkit/command/Command.java @@ -3046,26 +3000,34 @@ index 2fae50a9d1f0d9ecd91036697dedd64bc56f7d3b..bf299cfe88c383d489de0c36fd9a4922 return ret; } diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java -index 5fbacfcf108432c5187aa9a4092d00d7d5b0fd53..735e8f8591fec1d7108ac020cb2ddcf99577bce1 100644 +index 5fbacfcf108432c5187aa9a4092d00d7d5b0fd53..1a2949d872c8acd3c30c403ef7d8674fe086751c 100644 --- a/src/main/java/org/spigotmc/CustomTimingsHandler.java +++ b/src/main/java/org/spigotmc/CustomTimingsHandler.java -@@ -26,9 +26,6 @@ package org.spigotmc; - import org.bukkit.Bukkit; +@@ -23,45 +23,20 @@ + */ + package org.spigotmc; + +-import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; - import org.bukkit.plugin.AuthorNagException; +-import org.bukkit.plugin.AuthorNagException; -import co.aikar.timings.Timing; -import co.aikar.timings.Timings; -import co.aikar.timings.TimingsManager; - - import java.lang.reflect.Method; - import java.util.logging.Level; -@@ -39,29 +36,16 @@ import java.util.logging.Level; - * If you use this, migrate ASAP as this will be removed in the future! - * - * @deprecated -- * @see co.aikar.timings.Timings#of - */ +- +-import java.lang.reflect.Method; +-import java.util.logging.Level; +- +// Leaf start - Remove Timings + /** +- * This is here for legacy purposes incase any plugin used it. +- * +- * If you use this, migrate ASAP as this will be removed in the future! +- * +- * @deprecated +- * @see co.aikar.timings.Timings#of ++ * @deprecated Timings will be removed in the future ++ * Keep this class for plugin compatibility (detect usage) + */ @Deprecated(forRemoval = true) public final class CustomTimingsHandler { - private final Timing handler;