From 510ddf3bbcf88e5b401ba42db2a3ca35d9fcbbb6 Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sun, 15 Jun 2025 10:40:02 +0900 Subject: [PATCH 1/8] fix FasterRandomSource#next generate same random number (#374) --- .../dreeam/leaf/util/math/random/FasterRandomSource.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/math/random/FasterRandomSource.java b/leaf-server/src/main/java/org/dreeam/leaf/util/math/random/FasterRandomSource.java index ee6d5dc5..301a66ee 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/math/random/FasterRandomSource.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/math/random/FasterRandomSource.java @@ -56,11 +56,7 @@ public class FasterRandomSource implements BitRandomSource { @Override public final int next(int bits) { - if (useDirectImpl) { - return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits)); - } - - return (int) ((seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits)); + return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits)); } public static class FasterRandomSourcePositionalRandomFactory implements PositionalRandomFactory { From 04543a97f309e519cea0363af76058095dddda6e Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Mon, 16 Jun 2025 20:28:09 +0900 Subject: [PATCH 2/8] cleanup & remove threshold in async target finding --- .../features/0149-Async-target-finding.patch | 409 +++++++----------- .../features/0191-Paw-optimization.patch | 6 +- .../leaf/async/ai/AsyncGoalExecutor.java | 75 +--- .../dreeam/leaf/async/ai/AsyncGoalThread.java | 19 +- .../modules/async/AsyncTargetFinding.java | 7 +- 5 files changed, 189 insertions(+), 327 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0149-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0149-Async-target-finding.patch index 86cbd622..8d3d7b06 100644 --- a/leaf-server/minecraft-patches/features/0149-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0149-Async-target-finding.patch @@ -134,7 +134,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 fe81b2acfb51ed3335bde6f27ecfc53e339d2c7e..7955a8fa9c4de139b24c9d53018b055ff4008e02 100644 +index fe81b2acfb51ed3335bde6f27ecfc53e339d2c7e..53ea97c91684e9a0a3ed9f94ac15c242a43434ec 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 @@ -163,26 +163,13 @@ index fe81b2acfb51ed3335bde6f27ecfc53e339d2c7e..7955a8fa9c4de139b24c9d53018b055f public LevelChunk getChunkIfLoaded(int x, int z) { return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately -@@ -336,6 +346,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); - } - -+ // Leaf start - Async target finding -+ public final void leafMidTickTasks() { -+ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.midTick(); -+ } -+ // Leaf end - Async target finding -+ - @Override - public final ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final net.minecraft.world.level.chunk.status.ChunkStatus status) { - return this.moonrise$getChunkTaskScheduler().syncLoadNonFull(chunkX, chunkZ, status); -@@ -712,6 +728,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -712,6 +722,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle this.realPlayers = Lists.newArrayList(); // Leaves - skip this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.dreeam.leaf.async.world.SparklyPaperServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking + // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { -+ this.asyncGoalExecutor = new org.dreeam.leaf.async.ai.AsyncGoalExecutor(server.asyncGoalThread, this); ++ this.asyncGoalExecutor = new org.dreeam.leaf.async.ai.AsyncGoalExecutor(this); + } else { + this.asyncGoalExecutor = null; + } @@ -190,39 +177,8 @@ index fe81b2acfb51ed3335bde6f27ecfc53e339d2c7e..7955a8fa9c4de139b24c9d53018b055f } // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads -@@ -985,12 +1008,14 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - this.moonrise$midTickTasks(); // Paper - rewrite chunk system - // Gale end - Airplane - remove lambda from ticking guard - copied from guardEntityTick -+ this.leafMidTickTasks(); // Leaf - Async target finding - } - } - } - } - ); - this.tickBlockEntities(); -+ if (this.asyncGoalExecutor != null) this.asyncGoalExecutor.tick(); // Leaf - Async target finding - } - - // Paper - rewrite chunk system -@@ -1463,6 +1488,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - // Paper end - rewrite chunk system - -+ this.leafMidTickTasks(); // Leaf - Async target finding - } - - private void tickBlock(BlockPos pos, Block block) { -@@ -1479,6 +1505,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - // Paper end - rewrite chunk system - -+ this.leafMidTickTasks(); // Leaf - Async target finding - } - - // Paper start - log detailed entity tick information diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 9bee3c8e13b1d5d66139ed793fcd9bb154987cd0..7c095fd1d117bc0eeb18799943f1c1442219fd73 100644 +index 9bee3c8e13b1d5d66139ed793fcd9bb154987cd0..38b9931def2d407a3faa855db564e1d9ec1e636c 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 @@ -238,68 +194,39 @@ index 9bee3c8e13b1d5d66139ed793fcd9bb154987cd0..7c095fd1d117bc0eeb18799943f1c144 protected Mob(EntityType entityType, Level level) { super(entityType, level); -@@ -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 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(); - } -+ this.tickingTarget = true; +@@ -231,6 +237,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab 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 (!running && (this.targetSelector.ctxState != -1 || this.goalSelector.ctxState != -1)) { -+ ((ServerLevel) this.level()).asyncGoalExecutor.submit(this.getId()); -+ } ++ ((ServerLevel) this.level()).asyncGoalExecutor.tickMob(this); + } + // Leaf end - Async target finding } // Paper end -@@ -900,17 +916,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.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 - this.targetSelector.tickRunningGoals(false); -+ this.tickingTarget = false; - if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.goalSelector.tickRunningGoals(false); - } else { -+ this.tickingTarget = true; - if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking - this.targetSelector.tick(); -+ this.tickingTarget = false; +@@ -911,6 +922,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tick(); } ++ // Leaf start - Async target finding + 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()); -+ } ++ ((ServerLevel) this.level()).asyncGoalExecutor.tickMob(this); + } + // Leaf end - Async target finding 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..9abb8e7b0dea2cb63dad234812d773403d0716f6 100644 +index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..22f052d02ccf22f22894a0b236af1b95c8d65e97 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 { @Override public boolean canUse() { -+ // Leaf start - Async Avoid Entity Finding ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + if (!poll()) { + getNearestEntityAsync(); @@ -313,11 +240,11 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..9abb8e7b0dea2cb63dad234812d77340 this.mob, this.mob.getX(), - this.mob.getY(), -+ this.mob.getEyeY(), // Leaf - Async Avoid Entity Finding ++ this.mob.getEyeY(), // Leaf - Async target finding this.mob.getZ() ); + } -+ // Leaf end - Async Avoid Entity Finding ++ // Leaf end - Async target finding if (this.toAvoid == null) { return false; } else { @@ -325,7 +252,7 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..9abb8e7b0dea2cb63dad234812d77340 } } -+ // Leaf start - Async Avoid Entity Finding ++ // Leaf start - Async target finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + var serverLevel = getServerLevel(this.mob); @@ -353,20 +280,20 @@ index 4b24bb47a53a46586a642f3fb2656b1b8b670bf2..9abb8e7b0dea2cb63dad234812d77340 + z + ); + } -+ // Leaf end - Async Avoid Entity Finding ++ // Leaf end - Async target finding + @Override 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..d011e4735cb8fd65a39a6b7a66386375b12aca78 100644 +index 28ef40e8a645989ea181297069cf2bbe571f3082..661ff2379dca47162941f7ed2b6fb966a65f4ffc 100644 --- a/net/minecraft/world/entity/ai/goal/BegGoal.java +++ b/net/minecraft/world/entity/ai/goal/BegGoal.java @@ -27,8 +27,43 @@ public class BegGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.LOOK)); } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.wolf.getGoalCtx().result() instanceof Player target)) return false; + if (target == null) return false; @@ -390,11 +317,11 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..d011e4735cb8fd65a39a6b7a66386375 + return null; + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + @Override public boolean canUse() { -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -402,7 +329,7 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..d011e4735cb8fd65a39a6b7a66386375 + findTargetAsync(); + return false; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding this.player = this.level.getNearestPlayer(this.begTargeting, this.wolf); return this.player != null && this.playerHoldingInteresting(this.player); } @@ -411,16 +338,16 @@ index 28ef40e8a645989ea181297069cf2bbe571f3082..d011e4735cb8fd65a39a6b7a66386375 } - private boolean playerHoldingInteresting(Player player) { -+ private static boolean playerHoldingInteresting(Player player) { // Leaf start - Async Target Finding - static ++ private static boolean playerHoldingInteresting(Player player) { // Leaf start - Async target finding - static for (InteractionHand interactionHand : InteractionHand.values()) { ItemStack itemInHand = player.getItemInHand(interactionHand); - if (itemInHand.is(Items.BONE) || this.wolf.isFood(itemInHand)) { -+ if (itemInHand.is(Items.BONE) || itemInHand.is(net.minecraft.tags.ItemTags.WOLF_FOOD)) { // Leaf end - Async Target Finding ++ if (itemInHand.is(Items.BONE) || itemInHand.is(net.minecraft.tags.ItemTags.WOLF_FOOD)) { // Leaf end - Async target finding return true; } } diff --git a/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java b/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java -index 4c46cd105cde3cbcde65a02ff691c3a8edd56c76..b3205c9f35687bc37124876198ec2d657ccaa96c 100644 +index 4c46cd105cde3cbcde65a02ff691c3a8edd56c76..892e22b80d8c98ea2954b4024ba434da5a1abffa 100644 --- a/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java +++ b/net/minecraft/world/entity/ai/goal/CatLieOnBedGoal.java @@ -52,6 +52,13 @@ public class CatLieOnBedGoal extends MoveToBlockGoal { @@ -428,18 +355,18 @@ index 4c46cd105cde3cbcde65a02ff691c3a8edd56c76..b3205c9f35687bc37124876198ec2d65 @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(BlockTags.BEDS); -+ return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(BlockTags.BEDS); // Leaf - Async search block - diff on change ++ return level.isEmptyBlock(pos.above()) && level.getBlockState(pos).is(BlockTags.BEDS); // Leaf - Async target finding - diff on change } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.CatLie; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } diff --git a/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java b/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java -index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d35a172a3b 100644 +index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..79e71a41245028042b8ac5a56cd39bd0940c37f5 100644 --- a/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/CatSitOnBlockGoal.java @@ -44,14 +44,21 @@ public class CatSitOnBlockGoal extends MoveToBlockGoal { @@ -447,11 +374,11 @@ index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d3 @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - if (!level.isEmptyBlock(pos.above())) { -+ if (!level.isEmptyBlock(pos.above())) { // Leaf - Async search block - diff on change ++ if (!level.isEmptyBlock(pos.above())) { // Leaf - Async target finding - diff on change return false; } else { - BlockState blockState = level.getBlockState(pos); -+ BlockState blockState = level.getBlockState(pos); // Leaf - Async search block - diff on change ++ BlockState blockState = level.getBlockState(pos); // Leaf - Async target finding - diff on change return blockState.is(Blocks.CHEST) ? ChestBlockEntity.getOpenCount(level, pos) < 1 : blockState.is(Blocks.FURNACE) && blockState.getValue(FurnaceBlock.LIT) @@ -459,22 +386,22 @@ index 9954f49bc364969c7ccb37f4186fa2ab8710f6ae..057090e3134048e75dbaefb703e8f2d3 } } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.CatSit; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } diff --git a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java -index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52aa20a82d6 100644 +index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..70c75c0b3be9de58e73663c194fd18e8cc2989e7 100644 --- a/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowBoatGoal.java @@ -23,8 +23,47 @@ public class FollowBoatGoal extends Goal { this.mob = mob; } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player target)) return false; + var serverLevel = getServerLevel(this.mob); @@ -500,10 +427,10 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52a + return null; + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding @Override public boolean canUse() { -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -514,7 +441,7 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52a + findTargetAsync(); + return false; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding List entitiesOfClass = this.mob.level().getEntitiesOfClass(AbstractBoat.class, this.mob.getBoundingBox().inflate(5.0)); boolean flag = false; @@ -523,19 +450,19 @@ index 1fe4d1877ed52017d4d22ddb9b77f78e2b93dff9..92b6f87817aee8b277c07cf9138bf52a } - return this.following != null && (Mth.abs(this.following.xxa) > 0.0F || Mth.abs(this.following.zza) > 0.0F) || flag; -+ return flag; // Leaf - Async Target Finding - move above ++ return flag; // Leaf - Async target finding - move above } @Override diff --git a/net/minecraft/world/entity/ai/goal/FollowMobGoal.java b/net/minecraft/world/entity/ai/goal/FollowMobGoal.java -index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..c2baf746a0697559dc391b6f8c9303917e194836 100644 +index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..66f69163ed8d36dcd0dec5603e72a88e8812d3e5 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 { @Override public boolean canUse() { -+ // Leaf start - Async Follow Mob Finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -543,7 +470,7 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..c2baf746a0697559dc391b6f8c930391 + getFollowingMobAsync(); + return false; + } -+ // Leaf end - Async Follow Mob Finding ++ // Leaf end - Async target finding List entitiesOfClass = this.mob.level().getEntitiesOfClass(Mob.class, this.mob.getBoundingBox().inflate(this.areaSize), this.followPredicate); if (!entitiesOfClass.isEmpty()) { for (Mob mob : entitiesOfClass) { @@ -551,7 +478,7 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..c2baf746a0697559dc391b6f8c930391 return false; } -+ // Leaf start - Async Follow Mob Finding ++ // Leaf start - Async target finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Mob target)) return false; + var serverLevel = getServerLevel(this.mob); @@ -579,20 +506,20 @@ index c9cf985173d3da9e84ce178f161e2fd5fb8ce472..c2baf746a0697559dc391b6f8c930391 + return null; + }; + } -+ // Leaf end - Async Follow Mob Finding ++ // Leaf end - Async target finding + @Override 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..0a41797fd7beddce0b93d42bac6e0270169330ef 100644 +index 3093f03d4f298bf39fec8bad2b6c22518774aea8..4eec8eda6ea1698529e0392bc75c07be5980f5fc 100644 --- a/net/minecraft/world/entity/ai/goal/FollowParentGoal.java +++ b/net/minecraft/world/entity/ai/goal/FollowParentGoal.java @@ -19,11 +19,56 @@ public class FollowParentGoal extends Goal { this.speedModifier = speedModifier; } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.animal.getGoalCtx().result() instanceof Animal target)) return false; + var serverLevel = getServerLevel(animal); @@ -626,14 +553,14 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 + return target; + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + @Override public boolean canUse() { if (this.animal.getAge() >= 0) { return false; } else { -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -641,7 +568,7 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 + findTargetAsync(); + return false; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding List entitiesOfClass = this.animal .level() .getEntitiesOfClass((Class)this.animal.getClass(), this.animal.getBoundingBox().inflate(8.0, 4.0, 8.0)); @@ -649,12 +576,12 @@ index 3093f03d4f298bf39fec8bad2b6c22518774aea8..0a41797fd7beddce0b93d42bac6e0270 if (animal == null) { return false; } else if (d < 9.0) { -+ // Leaf - Async Target Finding - diff on change ++ // Leaf - Async target finding - diff on change return false; } 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..3c24382a3cced8dcea103ccc87cb506310de8461 100644 +index e82e32407cec6109b9c3b0106295217f4a3f4aa2..0998aa502b5a361558f1812bf93c2e01c8bb0ba8 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 { @@ -703,13 +630,13 @@ 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 +96,136 @@ public class GoalSelector { return true; } + // Leaf start - Async target finding + public final boolean poll() { -+ if (this.ctxGoals == null || ctx.wake != null) { ++ if (ctxState == -1 || this.ctxGoals == null || ctx.wake != null || ctxGoals.length == 0) { + return false; + } + if (ctxState == 0) { @@ -728,7 +655,12 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 + ctx.state = true; + } + -+ this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); ++ for (Goal.Flag flag : GOAL_FLAG_VALUES) { ++ var goal = this.lockedFlags.get(flag); ++ if (goal != null && !goal.isRunning()) { ++ this.lockedFlags.remove(flag); ++ } ++ } + + ctxIndex = 0; + ctx.state = true; @@ -776,7 +708,7 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 + 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(); ++ case net.minecraft.world.entity.ai.goal.target.ResetUniversalAngerTargetGoal t -> t.poll(); + default -> {} + } + } @@ -821,12 +753,12 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 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) { ++ if (this.ctxState == -1) { ++ if (this.availableGoalsDirty || this.ctxGoals == null) { + this.ctxGoals = this.availableGoals.toArray(new WrappedGoal[0]); -+ availableGoalsDirty = false; ++ this.availableGoalsDirty = false; + } -+ ctxState = 0; ++ this.ctxState = 0; + } + return; + } @@ -835,7 +767,7 @@ 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 +256,18 @@ public class GoalSelector { } public void tickRunningGoals(boolean tickAllRunning) { @@ -855,14 +787,14 @@ index e82e32407cec6109b9c3b0106295217f4a3f4aa2..3c24382a3cced8dcea103ccc87cb5063 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..86f041ff21cf44a0cded5744055654a0bff40c42 100644 +index be59d0c27a83b329ec3f97c029cfb9c114e22472..888fd1919954acf3ec3c5d5a115dffc2e0fd3caf 100644 --- a/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java +++ b/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java @@ -20,20 +20,83 @@ public class LlamaFollowCaravanGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE)); } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + private @javax.annotation.Nullable Llama poll() { + if (!(this.llama.getGoalCtx().result() instanceof Llama target)) return null; + var serverLevel = getServerLevel(this.llama); @@ -911,13 +843,13 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0 + return target; + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + @Override public boolean canUse() { if (!this.llama.level().purpurConfig.llamaJoinCaravans || !this.llama.shouldJoinCaravan) return false; // Purpur - Llama API // Purpur - Config to disable Llama caravans if (!this.llama.isLeashed() && !this.llama.inCaravan()) { -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + Llama llama = poll(); + double d = Double.MAX_VALUE; + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled) { @@ -928,20 +860,20 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0 + d = this.llama.distanceToSqr(llama); + } + } else { -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding List entities = this.llama.level().getEntities(this.llama, this.llama.getBoundingBox().inflate(9.0, 4.0, 9.0), entity1 -> { EntityType type = entity1.getType(); return type == EntityType.LLAMA || type == EntityType.TRADER_LLAMA; }); - Llama llama = null; - double d = Double.MAX_VALUE; -+ // Llama llama = null; // Leaf - Async Target Finding -+ // double d = Double.MAX_VALUE; // Leaf - Async Target Finding ++ // Llama llama = null; // Leaf - Async target finding ++ // double d = Double.MAX_VALUE; // Leaf - Async target finding for (Entity entity : entities) { Llama llama1 = (Llama)entity; - if (llama1.inCaravan() && !llama1.hasCaravanTail()) { -+ if (llama1.inCaravan() && !llama1.hasCaravanTail()) { // Leaf - Async Target Finding - diff on change ++ if (llama1.inCaravan() && !llama1.hasCaravanTail()) { // Leaf - Async target finding - diff on change double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; @@ -950,7 +882,7 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0 for (Entity entityx : entities) { Llama llama1 = (Llama)entityx; - if (llama1.isLeashed() && !llama1.hasCaravanTail()) { -+ if (llama1.isLeashed() && !llama1.hasCaravanTail()) { // Leaf - Async Target Finding - diff on change ++ if (llama1.isLeashed() && !llama1.hasCaravanTail()) { // Leaf - Async target finding - diff on change double d1 = this.llama.distanceToSqr(llama1); if (!(d1 > d)) { d = d1; @@ -958,19 +890,19 @@ index be59d0c27a83b329ec3f97c029cfb9c114e22472..86f041ff21cf44a0cded5744055654a0 } } } -+ } // Leaf - Async Target Finding ++ } // Leaf - Async target finding 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..98c2b4a298ada4b02afa55f991791d8696702181 100644 +index 6463c3c9b08d6058f2843c225b08a40fc30a960b..126bd98bc5980a2f1177bd7c74918b86a1f5bdce 100644 --- a/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java +++ b/net/minecraft/world/entity/ai/goal/LookAtPlayerGoal.java @@ -48,32 +48,79 @@ public class LookAtPlayerGoal extends Goal { @Override public boolean canUse() { -+ // Leaf start - Async look finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -1060,19 +992,19 @@ index 6463c3c9b08d6058f2843c225b08a40fc30a960b..98c2b4a298ada4b02afa55f991791d86 - } + }; } -+ // Leaf end - Async look finding ++ // Leaf end - Async target finding @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..9b8453ce2bc2cafca7c670d79b40434e7c93afca 100644 +index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..efd999c235271ac6b0935933db939cad51691a42 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -41,8 +41,60 @@ public abstract class MoveToBlockGoal extends Goal { this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.JUMP)); } -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof BlockPos blockPos1)) return false; + if (!this.mob.level().hasChunkAt(blockPos1) @@ -1117,15 +1049,15 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e + TurtleToWater, + TurtleLay, + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding + @Override public boolean canUse() { -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + if (poll()) { + return true; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding if (this.nextStartTick > 0) { this.nextStartTick--; return false; @@ -1133,12 +1065,12 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e } protected boolean findNearestBlock() { -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchBlock) { + getBlockAsync(); + return false; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding int i = this.searchRange; int i1 = this.verticalSearchRange; BlockPos blockPos = this.mob.blockPosition(); @@ -1146,7 +1078,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e return false; } -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + protected static @javax.annotation.Nullable BlockPos findNearestBlockAsync( + final TypeToCheck ty, + @org.jetbrains.annotations.Nullable final net.minecraft.world.level.block.Block toRemove, @@ -1180,11 +1112,11 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e + private static boolean isWithinRestriction(float restrictRadius, BlockPos restrictCenter, BlockPos pos) { + return restrictRadius == -1.0F || restrictCenter.distSqr(pos) < restrictRadius * restrictRadius; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding + protected abstract boolean isValidTarget(LevelReader level, BlockPos pos); + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + protected abstract TypeToCheck typeToCheck(); + + private static boolean isValidTargetAsync( @@ -1245,18 +1177,18 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..9b8453ce2bc2cafca7c670d79b40434e + } + case null -> throw new IllegalStateException(); + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding + } } diff --git a/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java b/net/minecraft/world/entity/ai/goal/OfferFlowerGoal.java -index 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80691ac1bc 100644 +index 3c274d917bca9de87abfb842f5f332e112a7a2d7..a3f96a6dac65a315842a4ae5de6c5ea04b7bd697 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 { @Override public boolean canUse() { -+ // Leaf start - Async offer flower finding ++ // Leaf start - Async target finding if (!this.golem.level().isDay()) { return false; - } else if (this.golem.getRandom().nextInt(8000) != 0) { @@ -1270,7 +1202,7 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80 + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + getVillagerAsync(); + return false; -+ // Leaf end - Async offer flower finding ++ // Leaf end - Async target finding } else { this.villager = getServerLevel(this.golem) .getNearestEntity( @@ -1279,7 +1211,7 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80 } + -+ // Leaf start - Async look finding ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.golem.getGoalCtx().result() instanceof Villager target)) return false; + var serverLevel = getServerLevel(this.golem); @@ -1306,13 +1238,13 @@ index 3c274d917bca9de87abfb842f5f332e112a7a2d7..2491b84641443ecfb8afc3b179e1cf80 + z + ); + } -+ // Leaf end - Async look finding ++ // Leaf end - Async target finding + @Override public boolean canContinueToUse() { return this.tick > 0; diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -index c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700b4c8a02d 100644 +index c67a88c9c77ece7c85ffb169ac96da4f28291228..a70fbb0f613befcba5daa961d941c6d40c3c356f 100644 --- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java @@ -37,7 +37,14 @@ public class RemoveBlockGoal extends MoveToBlockGoal { @@ -1321,12 +1253,12 @@ index c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700 return false; - } else if (this.nextStartTick > 0) { + } -+ // Leaf start - async search block ++ // Leaf start - Async target finding + if (poll()) { + this.nextStartTick = reducedTickDelay(20); + return true; + } -+ // Leaf end - async search block ++ // Leaf end - Async target finding + if (this.nextStartTick > 0) { this.nextStartTick--; return false; @@ -1336,27 +1268,27 @@ index c67a88c9c77ece7c85ffb169ac96da4f28291228..14d9b492ba431d534e0c6a567d0b7700 ChunkAccess chunk = level.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper - Prevent AI rules from loading chunks return chunk != null - && chunk.getBlockState(pos).is(this.blockToRemove) -+ && chunk.getBlockState(pos).is(this.blockToRemove) // Leaf - Async search block - diff on change ++ && chunk.getBlockState(pos).is(this.blockToRemove) // Leaf - Async target finding - diff on change && chunk.getBlockState(pos.above()).isAir() && chunk.getBlockState(pos.above(2)).isAir(); } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.RemoveBlock; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } diff --git a/net/minecraft/world/entity/ai/goal/TemptGoal.java b/net/minecraft/world/entity/ai/goal/TemptGoal.java -index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50e9e62b34 100644 +index f88f618d34fb343b31de3af1a875d6633703df71..4bbd83c702a818b48313698919dc9a85392707f6 100644 --- a/net/minecraft/world/entity/ai/goal/TemptGoal.java +++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java @@ -36,12 +36,51 @@ public class TemptGoal extends Goal { this.targetingConditions = TEMPT_TARGETING.copy().selector((entity, level) -> this.shouldFollow(entity)); } -+ // Leaf start - Async Tempt Finding ++ // Leaf start - Async target finding + private boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player target)) return false; + var serverLevel = getServerLevel(this.mob); @@ -1375,14 +1307,14 @@ index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50 + .copy(); + ctx.wake = () -> serverLevel.getNearestPlayer(conditions, mob); + } -+ // Leaf end - Async Tempt Finding ++ // Leaf end - Async target finding @Override public boolean canUse() { if (this.calmDown > 0) { this.calmDown--; return false; } else { -+ // Leaf start - Async Tempt Finding ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + if (poll()) { + if (this.player != null) { @@ -1400,28 +1332,19 @@ index f88f618d34fb343b31de3af1a875d6633703df71..754c379b42cf65c1d2278b474cdfbe50 + return false; + } + } -+ // Leaf end - Async Tempt Finding ++ // Leaf end - Async target finding 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/target/DefendVillageTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/DefendVillageTargetGoal.java -index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7abf8559358 100644 +index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..6f55599d206d8d6cdabe4e2a00dddb98448e498f 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 { - private final IronGolem golem; - @Nullable - private LivingEntity potentialTarget; -- private final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0); -+ private static final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0); // Leaf - Async Target Finding - static - - public DefendVillageTargetGoal(IronGolem golem) { - super(golem, false, true); @@ -24,8 +24,49 @@ public class DefendVillageTargetGoal extends TargetGoal { this.setFlags(EnumSet.of(Goal.Flag.TARGET)); } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + ServerLevel serverLevel = getServerLevel(this.mob); @@ -1451,7 +1374,7 @@ index 4644f3f7af89623ca6218c0dd24bb6cd67db553b..c9750ad322ddaa9c457f0e652d87c7ab + return null; + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + @Override public boolean canUse() { @@ -1477,7 +1400,7 @@ 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..e306c1cfc44878ea130d8046b31cf617aa32c3cc 100644 +index 25fe78116ce01eeefe5c958423734195d27302eb..b57f308963550584c73aaf8027061ac140a827a1 100644 --- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java @@ -73,6 +73,46 @@ public class HurtByTargetGoal extends TargetGoal { @@ -1485,7 +1408,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 double followDistance = this.getFollowDistance(); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(followDistance, 10.0, followDistance); + -+ // Leaf start - Async alert other ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.alertOther) { + final var self = this.mob; + final var ctx = self.getGoalCtx(); @@ -1522,7 +1445,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 + }; + return; + } -+ // Leaf end - Async alert other ++ // Leaf end - Async target finding + List entitiesOfClass = this.mob .level() @@ -1532,7 +1455,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 mob = (Mob)var5.next(); if (this.mob != mob - && mob.getTarget() == null -+ && mob.getTarget() == null // Leaf - Async alert other - diff on change ++ && mob.getTarget() == null // Leaf - Async target finding - diff on change && (!(this.mob instanceof TamableAnimal) || ((TamableAnimal)this.mob).getOwner() == ((TamableAnimal)mob).getOwner()) && !mob.isAlliedTo(this.mob.getLastHurtByMob())) { if (this.toIgnoreAlert == null) { @@ -1541,7 +1464,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 boolean flag = false; - for (Class clazz : this.toIgnoreAlert) { -+ for (Class clazz : this.toIgnoreAlert) { // Leaf - Async alert other - diff on change ++ for (Class clazz : this.toIgnoreAlert) { // Leaf - Async target finding - diff on change if (mob.getClass() == clazz) { flag = true; break; @@ -1549,7 +1472,7 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 } } -+ // Leaf start - Async alert other ++ // Leaf start - Async target finding + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toAlert)) return; + LivingEntity lastHurtByMob = this.mob.getLastHurtByMob(); @@ -1577,20 +1500,20 @@ index 25fe78116ce01eeefe5c958423734195d27302eb..e306c1cfc44878ea130d8046b31cf617 + } + } + } -+ // Leaf end - Async alert other ++ // Leaf end - Async target finding + protected void alertOther(Mob mob, LivingEntity target) { 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..f59d5c9be0eb10f5b5192442e1850900d71a31e9 100644 +index 85eae0a14f7a417dfd8c911079d05354a98e5834..0f20ad9dd776a057cf4a34fc9572181b0153b94f 100644 --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java @@ -41,8 +41,43 @@ public class NearestAttackableTargetGoal extends TargetG this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(selector); } -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof LivingEntity target)) return false; + ServerLevel serverLevel = getServerLevel(this.mob); @@ -1618,7 +1541,7 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..f59d5c9be0eb10f5b5192442e1850900 + } + }; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + @Override public boolean canUse() { @@ -1635,13 +1558,13 @@ index 85eae0a14f7a417dfd8c911079d05354a98e5834..f59d5c9be0eb10f5b5192442e1850900 protected void findTarget() { ServerLevel serverLevel = getServerLevel(this.mob); + -+ // Leaf start - Async Target Finding ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.searchEntity) { + this.findTargetAsync(); + this.target = null; + return; + } -+ // Leaf end - Async Target Finding ++ // Leaf end - Async target finding + if (this.targetType != Player.class && this.targetType != ServerPlayer.class) { this.target = serverLevel.getNearestEntity( @@ -1689,14 +1612,14 @@ 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..270f0b8b33aed1c54edbdb8595ce7fcc7fca2dc4 100644 +index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..08a09dea0dd377adca22b4432cf8f57c3122ec02 100644 --- a/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/ResetUniversalAngerTargetGoal.java @@ -37,6 +37,27 @@ public class ResetUniversalAngerTargetGoal extends G this.lastHurtByPlayerTimestamp = this.mob.getLastHurtByMobTimestamp(); this.mob.forgetCurrentTargetAndRefreshUniversalAnger(); if (this.alertOthersOfSameType) { -+ // Leaf start - Async alert other ++ // Leaf start - Async target finding + if (org.dreeam.leaf.config.modules.async.AsyncTargetFinding.alertOther) { + final var mob = this.mob; + final var ctx = mob.getGoalCtx(); @@ -1716,7 +1639,7 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..270f0b8b33aed1c54edbdb8595ce7fcc + }; + return; + } -+ // Leaf end - Async alert other ++ // Leaf end - Async target finding this.getNearbyMobsOfSameType() .stream() .filter(mob -> mob != this.mob) @@ -1724,7 +1647,7 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..270f0b8b33aed1c54edbdb8595ce7fcc super.start(); } -+ // Leaf start - Async alert other ++ // Leaf start - Async target finding + public void poll() { + if (!(this.mob.getGoalCtx().result() instanceof List toStop)) return; + for (var neutralMob : toStop) { @@ -1733,10 +1656,10 @@ index 61a7bb5f1760d47a13b6aafc123bcf3dff420860..270f0b8b33aed1c54edbdb8595ce7fcc + } + } + } -+ // Leaf end - Async alert other ++ // Leaf end - Async target finding + private List getNearbyMobsOfSameType() { -+ // Leaf - Async alert other - diff on change ++ // Leaf - Async target finding - diff on change double attributeValue = this.mob.getAttributeValue(Attributes.FOLLOW_RANGE); AABB aabb = AABB.unitCubeFromLowerCorner(this.mob.position()).inflate(attributeValue, 10.0, attributeValue); return this.mob.level().getEntitiesOfClass((Class)this.mob.getClass(), aabb, EntitySelector.NO_SPECTATORS); @@ -1781,7 +1704,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..54044ff16e0c2963b2220e4fb4932fe0802aa178 100644 +index 90452f0945e761077608692877677f522d38bccd..0a6ca6eb0775a7bc9e3070b70444c170a6504eca 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 { @@ -1809,7 +1732,7 @@ index 90452f0945e761077608692877677f522d38bccd..54044ff16e0c2963b2220e4fb4932fe0 protected boolean isValidTarget(LevelReader level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); - return blockState.is(Blocks.SWEET_BERRY_BUSH) && blockState.getValue(SweetBerryBushBlock.AGE) >= 2 || CaveVines.hasGlowBerries(blockState); -+ return blockState.is(Blocks.SWEET_BERRY_BUSH) && blockState.getValue(SweetBerryBushBlock.AGE) >= 2 || CaveVines.hasGlowBerries(blockState); // Leaf - Async search block - diff on change ++ return blockState.is(Blocks.SWEET_BERRY_BUSH) && blockState.getValue(SweetBerryBushBlock.AGE) >= 2 || CaveVines.hasGlowBerries(blockState); // Leaf - Async target finding - diff on change } @Override @@ -1818,24 +1741,24 @@ index 90452f0945e761077608692877677f522d38bccd..54044ff16e0c2963b2220e4fb4932fe0 super.start(); } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.FoxEat; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } class FoxFloatGoal extends FloatGoal { diff --git a/net/minecraft/world/entity/animal/Panda.java b/net/minecraft/world/entity/animal/Panda.java -index 0a6a3060f3690ab2d8439d66e6fd6f0c5dc20688..d99a1f6baebdc7c4a700e3fee30d1b52f3c1ac3d 100644 +index 0a6a3060f3690ab2d8439d66e6fd6f0c5dc20688..0fa38df5e2e11765a1d43ccc14b47730c83ae482 100644 --- a/net/minecraft/world/entity/animal/Panda.java +++ b/net/minecraft/world/entity/animal/Panda.java @@ -991,9 +991,18 @@ public class Panda extends Animal { @Override public boolean canUse() { -+ // Leaf start - Async look finding ++ // Leaf start - Async target finding + if (poll()) { + return true; + } @@ -1846,12 +1769,12 @@ index 0a6a3060f3690ab2d8439d66e6fd6f0c5dc20688..d99a1f6baebdc7c4a700e3fee30d1b52 + getLookAsync(); + return false; + } -+ // Leaf end - Async look finding ++ // Leaf end - Async target finding if (this.lookAt == null) { ServerLevel serverLevel = getServerLevel(this.mob); if (this.lookAtType == Player.class) { diff --git a/net/minecraft/world/entity/animal/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java -index da5b32a17283e540615373097acc511d928aeff5..cdab995c86978f0a789c85d14151abf19d89b673 100644 +index da5b32a17283e540615373097acc511d928aeff5..6211c6e41161852c06330f61b21969ac9a49cad8 100644 --- a/net/minecraft/world/entity/animal/Rabbit.java +++ b/net/minecraft/world/entity/animal/Rabbit.java @@ -642,7 +642,13 @@ public class Rabbit extends Animal implements VariantHolder { @@ -1859,13 +1782,13 @@ index da5b32a17283e540615373097acc511d928aeff5..cdab995c86978f0a789c85d14151abf1 } - return super.canUse(); -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + if (this.wantsToRaid && !this.canRaid) { + return super.canUse(); + } else { + return false; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } @Override @@ -1874,7 +1797,7 @@ index da5b32a17283e540615373097acc511d928aeff5..cdab995c86978f0a789c85d14151abf1 protected boolean isValidTarget(LevelReader level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); - if (blockState.is(Blocks.FARMLAND) && this.wantsToRaid && !this.canRaid) { -+ if (blockState.is(Blocks.FARMLAND) && this.wantsToRaid && !this.canRaid) { // Leaf - Async search block - diff on change ++ if (blockState.is(Blocks.FARMLAND) && this.wantsToRaid && !this.canRaid) { // Leaf - Async target finding - diff on change blockState = level.getBlockState(pos.above()); if (blockState.getBlock() instanceof CarrotBlock && ((CarrotBlock)blockState.getBlock()).isMaxAge(blockState)) { this.canRaid = true; @@ -1883,17 +1806,17 @@ index da5b32a17283e540615373097acc511d928aeff5..cdab995c86978f0a789c85d14151abf1 return false; } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.RaidGarden; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } public static enum Variant implements StringRepresentable { diff --git a/net/minecraft/world/entity/animal/Turtle.java b/net/minecraft/world/entity/animal/Turtle.java -index 10477fea8fcd70bf0c1ba4b6e1113625be690e68..c5f626dcfe724f8ed19303d305f6eb5cfcc0f0af 100644 +index 10477fea8fcd70bf0c1ba4b6e1113625be690e68..a7c1f02e3e7346555d7f822acfb15b6dea312dd5 100644 --- a/net/minecraft/world/entity/animal/Turtle.java +++ b/net/minecraft/world/entity/animal/Turtle.java @@ -527,8 +527,15 @@ public class Turtle extends Animal { @@ -1901,15 +1824,15 @@ index 10477fea8fcd70bf0c1ba4b6e1113625be690e68..c5f626dcfe724f8ed19303d305f6eb5c @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.getBlockState(pos).is(Blocks.WATER); -+ return level.getBlockState(pos).is(Blocks.WATER); // Leaf - Async search block - diff on change ++ return level.getBlockState(pos).is(Blocks.WATER); // Leaf - Async target finding - diff on change } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.TurtleToWater; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } static class TurtleLayEggGoal extends MoveToBlockGoal { @@ -1918,20 +1841,20 @@ index 10477fea8fcd70bf0c1ba4b6e1113625be690e68..c5f626dcfe724f8ed19303d305f6eb5c @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.isEmptyBlock(pos.above()) && TurtleEggBlock.isSand(level, pos); -+ return level.isEmptyBlock(pos.above()) && TurtleEggBlock.isSand(level, pos); // Leaf - Async search block - diff on change ++ return level.isEmptyBlock(pos.above()) && TurtleEggBlock.isSand(level, pos); // Leaf - Async target finding - diff on change + } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.TurtleLay; } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } static class TurtleMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables diff --git a/net/minecraft/world/entity/animal/Wolf.java b/net/minecraft/world/entity/animal/Wolf.java -index 7cb292de6b27fa4ba3c5fce526a4e939c576789f..68852158e2fd3c4ce241d722ac414d73ea3b8836 100644 +index 7cb292de6b27fa4ba3c5fce526a4e939c576789f..187707845c4ed8cc3be439a12633ba9c446d075f 100644 --- a/net/minecraft/world/entity/animal/Wolf.java +++ b/net/minecraft/world/entity/animal/Wolf.java @@ -672,7 +672,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder level.getNearestPlayer(cond, enderman); + return false; + } -+ // Leaf end - Async Target Finding ++ // 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 ++ // Leaf start - Async target finding + protected boolean poll() { + if (!(this.mob.getGoalCtx().result() instanceof Player player)) return false; + var serverLevel = getServerLevel(this.enderman); @@ -2004,13 +1927,13 @@ index c7897532163d4fdf5a82982f7d24a47dd61e3dfa..2986384389c1acd311f4367d62255083 + this.pendingTarget = player; + return true; + } -+ // Leaf end - Async Target Finding ++ // 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 +index ae4ee948971e931e4fdc4ec2187f5182195c626c..041079bf35822e75b5464a6336f517b38cb19363 100644 --- a/net/minecraft/world/entity/monster/Strider.java +++ b/net/minecraft/world/entity/monster/Strider.java @@ -570,8 +570,15 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { @@ -2018,31 +1941,23 @@ index ae4ee948971e931e4fdc4ec2187f5182195c626c..f4fa19c6352e44a624e81dc201b1d7d7 @Override protected boolean isValidTarget(LevelReader level, BlockPos pos) { - return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(PathComputationType.LAND); -+ return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(PathComputationType.LAND); // Leaf - Async search block - diff on change ++ return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(PathComputationType.LAND); // Leaf - Async target finding - diff on change } + -+ // Leaf start - Async search block ++ // Leaf start - Async target finding + @Override + protected TypeToCheck typeToCheck() { + return TypeToCheck.Strider; + } -+ // Leaf end - Async search block ++ // Leaf end - Async target finding } static class StriderPathNavigation extends GroundPathNavigation { diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index ac7729d1caa80155697bfe0e8646e4eda5d1780e..2518ea7a9d2122ff55ab6546d21d7ed6cc933090 100644 +index ac7729d1caa80155697bfe0e8646e4eda5d1780e..c8872d7b8dbf368f401b6c36b8145c1d53ece0dd 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -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 - // Paper end - rewrite chunk system -+ ((ServerLevel) this).leafMidTickTasks(); // Leaf - Async target finding - } - } - this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals -@@ -1817,9 +1818,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1817,9 +1817,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Override public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { diff --git a/leaf-server/minecraft-patches/features/0191-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0191-Paw-optimization.patch index a735ae35..91d9e5fb 100644 --- a/leaf-server/minecraft-patches/features/0191-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0191-Paw-optimization.patch @@ -117,10 +117,10 @@ index c1efd558cfbfd2200295ef5755aa496e95deb7d7..15bbd1f7f2a90b4b5427026d622764bb 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 7955a8fa9c4de139b24c9d53018b055ff4008e02..eb849c57992658005e0f514c6f7923f8ca43bebf 100644 +index 53ea97c91684e9a0a3ed9f94ac15c242a43434ec..7cbcc2dd8533973108841d3989265a392a6f6214 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1521,13 +1521,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1511,13 +1511,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - log detailed entity tick information public void tickNonPassenger(Entity entity) { @@ -134,7 +134,7 @@ index 7955a8fa9c4de139b24c9d53018b055ff4008e02..eb849c57992658005e0f514c6f7923f8 entity.setOldPosAndRot(); entity.tickCount++; entity.totalEntityAge++; // Paper - age-like counter for all entities -@@ -1540,13 +1534,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1530,13 +1524,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe for (Entity entity1 : entity.getPassengers()) { this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 } 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 780e2678..371f54e5 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,6 +1,5 @@ package org.dreeam.leaf.async.ai; -import it.unimi.dsi.fastutil.ints.IntArrayList; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Mob; @@ -10,85 +9,54 @@ import org.dreeam.leaf.config.modules.async.AsyncTargetFinding; import org.dreeam.leaf.util.queue.SpscIntQueue; import java.util.OptionalInt; -import java.util.concurrent.locks.LockSupport; public class AsyncGoalExecutor { protected static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal"); protected final SpscIntQueue queue; - protected final SpscIntQueue wake; - protected final IntArrayList submit; private final ServerLevel world; - private long midTickCount = 0L; - public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) { + public AsyncGoalExecutor(ServerLevel world) { this.world = world; this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize); - this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize); - this.submit = new IntArrayList(); } - boolean wake(int id) { - Entity entity = this.world.getEntities().get(id); - if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { - return false; - } - mob.goalSelector.ctx.wake(); - mob.targetSelector.ctx.wake(); - return true; - } - - public final void submit(int entityId) { - this.submit.add(entityId); - } - - public final void tick() { - batchSubmit(); + boolean wakeAll() { + boolean success = false; while (true) { - OptionalInt result = this.wake.recv(); + OptionalInt result = queue.recv(); if (result.isEmpty()) { break; } int id = result.getAsInt(); - if (poll(id) && !this.queue.send(id)) { - do { - wake(id); - } while (poll(id)); - } + success = true; + wake(id); } + return success; } - private void batchSubmit() { - if (submit.isEmpty()) { + public void tickMob(Mob mob) { + if (!poll(mob)) { return; } - int[] raw = submit.elements(); - int size = submit.size(); - for (int i = 0; i < size; i++) { - int id = raw[i]; - if (poll(id) && !this.queue.send(id)) { - do { - wake(id); - } while (poll(id)); - } + int entityId = mob.getId(); + if (!this.queue.send(entityId)) { + do { + wake(entityId); + } while (poll(mob)); } - this.submit.clear(); } - public final void midTick() { - if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) { - batchSubmit(); - } - - midTickCount += 1; - } - - private boolean poll(int id) { + private void wake(int id) { Entity entity = this.world.getEntities().get(id); if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) { - return false; + return; } + mob.goalSelector.ctx.wake(); + mob.targetSelector.ctx.wake(); + } + private boolean poll(Mob mob) { try { mob.tickingTarget = true; boolean a = mob.targetSelector.poll(); @@ -97,8 +65,7 @@ public class AsyncGoalExecutor { return a || b; } catch (Exception e) { LOGGER.error("Exception while polling", e); - // retry - return true; + return false; } } } 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 e989adbf..4012e4cf 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 @@ -22,26 +22,11 @@ public class AsyncGoalThread extends Thread { while (RUNNING) { boolean retry = false; for (ServerLevel level : server.getAllLevels()) { - var exec = level.asyncGoalExecutor; - while (true) { - OptionalInt result = exec.queue.recv(); - if (result.isEmpty()) { - break; - } - int id = result.getAsInt(); - retry = true; - if (exec.wake(id)) { - while (!exec.wake.send(id)) { - Thread.onSpinWait(); - } - } - } - - Thread.yield(); + retry |= level.asyncGoalExecutor.wakeAll(); } if (!retry) { - LockSupport.parkNanos(10_000L); + LockSupport.parkNanos(1_000_000L); } } } 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 c22f1ce4..b4cea17b 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 @@ -15,7 +15,6 @@ public class AsyncTargetFinding extends ConfigModules { public static boolean searchBlock = true; public static boolean searchEntity = true; public static int queueSize = 4096; - public static long threshold = 10L; private static boolean asyncTargetFindingInitialized; @Override @@ -35,21 +34,17 @@ public class AsyncTargetFinding extends ConfigModules { enabled = config.getBoolean(getBasePath() + ".enabled", enabled); // Disable if parallel world ticking is enabled, as they are incompatible. if (enabled && SparklyPaperParallelWorldTicking.enabled) { - LeafConfig.LOGGER.warn("Async Target Finding is incompatible with Parallel World Ticking. Disabling Async Target Finding automatically."); + LeafConfig.LOGGER.warn("Async target finding is incompatible with Parallel World Ticking. Disabling Async target finding automatically."); enabled = false; } alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true); searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true); searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true); queueSize = config.getInt(getBasePath() + ".queue-size", 0); - threshold = config.getLong(getBasePath() + ".threshold", 0); if (queueSize <= 0) { queueSize = 4096; } - if (threshold == 0L) { - threshold = 10L; - } if (!enabled) { alertOther = false; searchEntity = false; From de2562294c1177847a2cdcfb9b090fbd32116f5f Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:10:51 +1400 Subject: [PATCH 3/8] Backport AsyncLocator fixes --- .../features/0089-Asynchronous-locator.patch | 88 +++++++++++++------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0089-Asynchronous-locator.patch b/leaf-server/minecraft-patches/features/0089-Asynchronous-locator.patch index ee20df93..1f71b727 100644 --- a/leaf-server/minecraft-patches/features/0089-Asynchronous-locator.patch +++ b/leaf-server/minecraft-patches/features/0089-Asynchronous-locator.patch @@ -7,48 +7,78 @@ Original license: MIT Original project: https://github.com/thebrightspark/AsyncLocator diff --git a/net/minecraft/server/commands/LocateCommand.java b/net/minecraft/server/commands/LocateCommand.java -index 13bcd8653d766cd0b754a22e9aab261fbc62b0a5..e3e7c4e4da0bc95b015bb84e470477782bdb691c 100644 +index 13bcd8653d766cd0b754a22e9aab261fbc62b0a5..13542755864904b343b0aeea957e2a76e977a51f 100644 --- a/net/minecraft/server/commands/LocateCommand.java +++ b/net/minecraft/server/commands/LocateCommand.java -@@ -109,6 +109,38 @@ public class LocateCommand { +@@ -109,6 +109,34 @@ public class LocateCommand { BlockPos blockPos = BlockPos.containing(source.getPosition()); ServerLevel level = source.getLevel(); Stopwatch stopwatch = Stopwatch.createStarted(Util.TICKER); + // Leaf start - Asynchronous locator + if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled) { -+ net.minecraft.commands.CommandSource locatorSource = source.source; -+ if (locatorSource instanceof net.minecraft.server.level.ServerPlayer || locatorSource instanceof net.minecraft.server.MinecraftServer) { -+ BlockPos originPos = BlockPos.containing(source.getPosition()); ++ BlockPos originPos = BlockPos.containing(source.getPosition()); ++ org.dreeam.leaf.async.locate.AsyncLocator.locate(source.getLevel(), holderSet, originPos, 100, false) ++ .thenOnServerThread(pair -> { ++ stopwatch.stop(); ++ if (pair != null) { ++ showLocateResult( ++ source, ++ structure, ++ originPos, ++ pair, ++ "commands.locate.structure.success", ++ false, ++ stopwatch.elapsed() ++ ); ++ } else { ++ source.sendFailure( ++ Component.literal( ++ ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable()).getMessage() ++ ) ++ ); ++ } ++ }); + -+ org.dreeam.leaf.async.locate.AsyncLocator.locate(source.getLevel(), holderSet, originPos, 100, false) -+ .thenOnServerThread(pair -> { -+ stopwatch.stop(); -+ if (pair != null) { -+ showLocateResult( -+ source, -+ structure, -+ originPos, -+ pair, -+ "commands.locate.structure.success", -+ false, -+ stopwatch.elapsed() -+ ); -+ } else { -+ source.sendFailure( -+ Component.literal( -+ ERROR_STRUCTURE_NOT_FOUND.create(structure.asPrintable()).getMessage() -+ ) -+ ); -+ } -+ }); -+ -+ return 0; -+ } ++ return 0; + } + // Leaf end - Asynchronous locator Pair> pair = level.getChunkSource().getGenerator().findNearestMapStructure(level, holderSet, blockPos, 100, false); stopwatch.stop(); if (pair == null) { +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 0fd51020ca9480be2855eb58a7d4d43511829512..52a2b993bbd1ad4851b3273af6ecbc069beb5b84 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -878,14 +878,25 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + + @Override + public boolean pollTask() { ++ // Leaf start - Async Locator + // Paper start - rewrite chunk system +- final ServerChunkCache serverChunkCache = ServerChunkCache.this; +- if (serverChunkCache.runDistanceManagerUpdates()) { +- return true; ++ java.util.function.Supplier supplier = () -> { ++ final ServerChunkCache serverChunkCache = ServerChunkCache.this; ++ if (serverChunkCache.runDistanceManagerUpdates()) { ++ return true; ++ } else { ++ return super.pollTask() | ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) serverChunkCache.level).moonrise$getChunkTaskScheduler().executeMainThreadTask(); ++ } ++ }; ++ if (org.dreeam.leaf.config.modules.async.AsyncLocator.enabled && Thread.currentThread() instanceof org.dreeam.leaf.async.locate.AsyncLocator.AsyncLocatorThread) { ++ return MinecraftServer.getServer().scheduleWithResult((java.util.concurrent.CompletableFuture future) -> { ++ future.complete(supplier.get()); ++ }).join(); + } else { +- return super.pollTask() | ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)serverChunkCache.level).moonrise$getChunkTaskScheduler().executeMainThreadTask(); ++ return supplier.get(); + } + // Paper end - rewrite chunk system ++ // Leaf end - Async Locator + } + } + } diff --git a/net/minecraft/world/entity/animal/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java index 7003b532182737a745491e397a967b72e6b308aa..3ed9652510976770f5661dd7b317f27f046700d4 100644 --- a/net/minecraft/world/entity/animal/Dolphin.java From 6d86da82c146743eceb3e8d528557a40b034601a Mon Sep 17 00:00:00 2001 From: Taiyou <77050201+Taiyou06@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:56:23 +0200 Subject: [PATCH 4/8] Optimize random tick (#359) * optimize random tick * rebase * fix random tick chance check * add back zero check fix always tick on first tick * optimize random check * cleanup * remove 0 to 2 random on amount chosen * cleanup * add queue and reuse block count * reduce LevelChunkSection#tickingBlocks memory overhead * Revert "reduce LevelChunkSection#tickingBlocks memory overhead" This reverts commit 942376481bf775324ce6349766200b5f044bffa0. * fix overflow * [ci skip] rebuild patches --------- Co-authored-by: hayanesuru --- .../features/0192-optimize-random-tick.patch | 105 +++++++++++ .../modules/opt/OptimizeRandomTick.java | 20 +++ .../dreeam/leaf/world/RandomTickSystem.java | 164 ++++++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java diff --git a/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch new file mode 100644 index 00000000..76b1f5a9 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0192-optimize-random-tick.patch @@ -0,0 +1,105 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Fri, 6 Jun 2025 20:46:10 +0900 +Subject: [PATCH] optimize random tick + + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index c80e6669013d3cf87b51d0cb0b62fcf5466d702c..9d132c88629976cc00d470980308571ac3222284 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -693,6 +693,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + this.level.tickChunk(levelChunk, _int); + } + } ++ this.level.randomTickSystem.tick(this.level); // Leaf - optimize random tick + + if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations + this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 7cbcc2dd8533973108841d3989265a392a6f6214..bc0c80ed78bbb594cf7281e8d87f27dd06a1fcbb 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1120,6 +1120,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking + ++ public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting + ChunkPos pos = chunk.getPos(); +@@ -1169,7 +1170,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } // Paper - Option to disable ice and snow + + if (randomTickSpeed > 0) { +- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking ++ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.tickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - random tick ++ else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - random tick + } + } + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index a90bf0d80ae4dac9b19b8e467b402917cc19a271..44b672671e0bea6a5f91e9b8573f9a8225a20f6a 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -149,6 +149,48 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + } + // Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively + ++ // Leaf start - optimize random tick ++ private boolean leaf$tickingBlocksDirty = true; ++ private int leaf$tickingBlocksCount; ++ private int leaf$firstTickingSectionIndex = -1; ++ public final int leaf$tickingBlocksCount() { ++ if (!leaf$tickingBlocksDirty) { ++ return leaf$tickingBlocksCount; ++ } ++ leaf$tickingBlocksDirty = false; ++ int sum = 0; ++ leaf$firstTickingSectionIndex = -1; ++ for (int i = 0; i < sections.length; i++) { ++ LevelChunkSection section = sections[i]; ++ int size = section.moonrise$getTickingBlockList().size(); ++ if (size != 0 && leaf$firstTickingSectionIndex == -1) { ++ leaf$firstTickingSectionIndex = i; ++ } ++ sum += size; ++ } ++ leaf$tickingBlocksCount = sum; ++ return sum; ++ } ++ public final java.util.OptionalLong leaf$getTickingPos(int idx) { ++ if (leaf$firstTickingSectionIndex != -1) { ++ for (int i = leaf$firstTickingSectionIndex; i < sections.length; i++) { ++ LevelChunkSection section = sections[i]; ++ var l = section.moonrise$getTickingBlockList(); ++ int size = l.size(); ++ if (idx < size) { ++ short loc = l.getRaw(idx); ++ int x = (loc & 15) | (chunkPos.x << 4); ++ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4); ++ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4); ++ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z)); ++ } ++ idx -= size; ++ } ++ } ++ leaf$tickingBlocksDirty = true; ++ return java.util.OptionalLong.empty(); ++ } ++ // Leaf end - optimize random tick + public LevelChunk(Level level, ChunkPos pos) { + this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); + } +@@ -417,6 +459,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + if (blockState == state) { + return null; + } else { ++ // Leaf start - optimize random tick ++ if (blockState.isRandomlyTicking() != state.isRandomlyTicking()) { ++ leaf$tickingBlocksDirty = true; ++ } ++ // Leaf end - optimize random tick + Block block = state.getBlock(); + this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state); + this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java new file mode 100644 index 00000000..525bcac7 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeRandomTick.java @@ -0,0 +1,20 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; + +public class OptimizeRandomTick extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".optimise-random-tick"; + } + + @Experimental + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath(), enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java new file mode 100644 index 00000000..fdcaad27 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -0,0 +1,164 @@ +package org.dreeam.leaf.world; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.material.FluidState; + +import java.util.OptionalLong; + +public final class RandomTickSystem { + private static final long SCALE = 0x100000L; + private static final long CHUNK_BLOCKS = 4096L; + + /// reduce unnecessary sampling and block counting + private static final long TICK_MASK = 0b11L; + private static final long TICK_MUL = 4L; + private static final int BITS_STEP = 2; + private static final int BITS_MAX = 60; + + private final LongArrayList queue = new LongArrayList(); + private final LongArrayList samples = new LongArrayList(); + private final LongArrayList weights = new LongArrayList(); + private long weightsSum = 0L; + + private int bits = 60; + private long cacheRandom = 0L; + + public void tick(ServerLevel world) { + if (weights.isEmpty() || samples.isEmpty()) { + return; + } + + final var random = world.simpleRandom; + final long chosen; + if (((weightsSum % SCALE) >= boundedNextLong(random, SCALE))) { + chosen = weightsSum / SCALE + 1L; + } else { + chosen = weightsSum / SCALE; + } + if (chosen == 0L) { + return; + } + + final long spoke = weightsSum / chosen; + if (spoke == 0L) { + return; + } + + final long[] weightsRaw = weights.elements(); + final long[] samplesRaw = samples.elements(); + + long accumulated = weightsRaw[0]; + long current = boundedNextLong(random, spoke); + int i = 0; + while (current < weightsSum) { + while (accumulated < current) { + i += 1; + accumulated += weightsRaw[i]; + } + queue.add(samplesRaw[i]); + current += spoke; + } + while (queue.size() < chosen) { + queue.add(samplesRaw[i]); + } + + long[] queueRaw = queue.elements(); + int j = 0; + int k; + for (k = queue.size() - 3; j < k; j += 4) { + final long packed1 = queueRaw[j]; + final long packed2 = queueRaw[j + 1]; + final long packed3 = queueRaw[j + 2]; + final long packed4 = queueRaw[j + 3]; + final LevelChunk chunk1 = getChunk(world, packed1); + final LevelChunk chunk2 = packed1 != packed2 ? getChunk(world, packed2) : chunk1; + final LevelChunk chunk3 = packed2 != packed3 ? getChunk(world, packed3) : chunk2; + final LevelChunk chunk4 = packed3 != packed4 ? getChunk(world, packed4) : chunk3; + if (chunk1 != null) tickBlock(world, chunk1, random); + if (chunk2 != null) tickBlock(world, chunk2, random); + if (chunk3 != null) tickBlock(world, chunk3, random); + if (chunk4 != null) tickBlock(world, chunk4, random); + } + for (k = queue.size(); j < k; j++) { + LevelChunk chunk = getChunk(world, queueRaw[j]); + if (chunk != null) tickBlock(world, chunk, random); + } + + weightsSum = 0L; + queue.clear(); + weights.clear(); + samples.clear(); + } + + private static LevelChunk getChunk(ServerLevel world, long packed) { + return world.chunkSource.getChunkAtIfLoadedImmediately((int) packed, (int) (packed >> 32)); + } + + private static void tickBlock(ServerLevel world, LevelChunk chunk, RandomSource random) { + OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(chunk.leaf$tickingBlocksCount())); + if (optionalPos.isEmpty()) { + return; + } + BlockPos pos = BlockPos.of(optionalPos.getAsLong()); + BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); + state.randomTick(world, pos, random); + + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { + fluidState.randomTick(world, pos, random); + } + } + } + + public void tickChunk( + RandomSource random, + LevelChunk chunk, + long tickSpeed + ) { + if (this.bits == BITS_MAX) { + this.bits = 0; + this.cacheRandom = random.nextLong(); + } else { + this.bits += BITS_STEP; + } + if ((this.cacheRandom & (TICK_MASK << bits)) == 0L) { + long count = chunk.leaf$tickingBlocksCount(); + if (count != 0L) { + long weight = (TICK_MUL * tickSpeed * count * SCALE) / CHUNK_BLOCKS; + samples.add(chunk.getPos().longKey); + weights.add(weight); + weightsSum += weight; + } + } + } + + /** + * @param rng a random number generator to be used as a + * source of pseudorandom {@code long} values + * @param bound the upper bound (exclusive); must be greater than zero + * + * @return a pseudorandomly chosen {@code long} value + * + * @see java.util.random.RandomGenerator#nextLong(long) nextLong(bound) + */ + public static long boundedNextLong(RandomSource rng, long bound) { + final long m = bound - 1; + long r = rng.nextLong(); + if ((bound & m) == 0L) { + r &= m; + } else { + for (long u = r >>> 1; + u + m - (r = u % bound) < 0L; + u = rng.nextLong() >>> 1) + ; + } + return r; + } +} From 91a911a60f64d6264311f5091d9046c4acf6c7cf Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:10:08 +0800 Subject: [PATCH 5/8] Fix powered rail range in optimized powered rail --- .../world/block/OptimizedPoweredRails.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/block/OptimizedPoweredRails.java b/leaf-server/src/main/java/org/dreeam/leaf/world/block/OptimizedPoweredRails.java index f4f3c7be..7e34b2e1 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/block/OptimizedPoweredRails.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/block/OptimizedPoweredRails.java @@ -20,8 +20,6 @@ public class OptimizedPoweredRails { private static final int UPDATE_FORCE_PLACE = UPDATE_MOVE_BY_PISTON | UPDATE_KNOWN_SHAPE | UPDATE_CLIENTS; - private static int RAIL_POWER_LIMIT = 8; - private static final Object2BooleanOpenHashMap CHECKED_POS_POOL = new Object2BooleanOpenHashMap<>(); private static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) { @@ -36,14 +34,6 @@ public class OptimizedPoweredRails { ); } - public static int getRailPowerLimit() { - return RAIL_POWER_LIMIT; - } - - public static void setRailPowerLimit(int powerLimit) { - RAIL_POWER_LIMIT = powerLimit; - } - public static void updateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) { boolean shouldBePowered = level.hasNeighborSignal(pos) || findPoweredRailSignalFaster(self, level, pos, state, true, 0, CHECKED_POS_POOL) || @@ -97,7 +87,7 @@ public class OptimizedPoweredRails { private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level, BlockPos pos, BlockState state, boolean searchForward, int distance, Object2BooleanOpenHashMap checkedPos) { - if (distance >= RAIL_POWER_LIMIT - 1) return false; + if (distance >= level.purpurConfig.railActivationRange) return false; int x = pos.getX(); int y = pos.getY(); int z = pos.getZ(); @@ -203,7 +193,8 @@ public class OptimizedPoweredRails { private static void setRailPositionsPower(PoweredRailBlock self, Level level, BlockPos pos, Object2BooleanOpenHashMap checkedPos, int[] count, int i, Direction dir) { - for (int z = 1; z < RAIL_POWER_LIMIT; z++) { + final int railPowerLimit = level.purpurConfig.railActivationRange; + for (int z = 1; z < railPowerLimit; z++) { BlockPos newPos = pos.relative(dir, z); BlockState state = level.getBlockState(newPos); if (checkedPos.containsKey(newPos)) { @@ -229,7 +220,8 @@ public class OptimizedPoweredRails { int[] count, int i, Direction dir) { Object2BooleanOpenHashMap checkedPos = CHECKED_POS_POOL; checkedPos.clear(); - for (int z = 1; z < RAIL_POWER_LIMIT; z++) { + final int railPowerLimit = level.purpurConfig.railActivationRange; + for (int z = 1; z < railPowerLimit; z++) { BlockPos newPos = pos.relative(dir, z); BlockState state = level.getBlockState(newPos); if (!state.is(self) || !state.getValue(POWERED) || level.hasNeighborSignal(newPos) || From 5311ae81c6ca989fdaaf38f6eddfcc0308d3c8ac Mon Sep 17 00:00:00 2001 From: hayanesuru Date: Sat, 21 Jun 2025 02:01:56 +0900 Subject: [PATCH 6/8] fix boundary of optimize random tick --- .../main/java/org/dreeam/leaf/world/RandomTickSystem.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java index fdcaad27..23967287 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/RandomTickSystem.java @@ -85,7 +85,7 @@ public final class RandomTickSystem { if (chunk4 != null) tickBlock(world, chunk4, random); } for (k = queue.size(); j < k; j++) { - LevelChunk chunk = getChunk(world, queueRaw[j]); + final LevelChunk chunk = getChunk(world, queueRaw[j]); if (chunk != null) tickBlock(world, chunk, random); } @@ -100,7 +100,11 @@ public final class RandomTickSystem { } private static void tickBlock(ServerLevel world, LevelChunk chunk, RandomSource random) { - OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(chunk.leaf$tickingBlocksCount())); + int count = chunk.leaf$tickingBlocksCount(); + if (count == 0) { + return; + } + OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(count)); if (optionalPos.isEmpty()) { return; } From 5bbafee8a1caa492a298bc7a74348ee0b942718d Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:00:38 +0800 Subject: [PATCH 7/8] Port `Avoid and discourage use of Maven Central as a CDN` from Paper & Cleanup `Configurable LibraryLoader maven repos` --- ...ourage-use-of-Maven-Central-as-a-CDN.patch | 112 ++++++++++++++++++ ...try.patch => 0004-Pufferfish-Sentry.patch} | 0 ...es.patch => 0005-Purpur-API-Changes.patch} | 0 ...imings.patch => 0006-Remove-Timings.patch} | 0 ...patch => 0007-KeYi-Player-Skull-API.patch} | 0 ...nfigurable-LibraryLoader-maven-repos.patch | 78 ------------ ...atch => 0008-Slice-Smooth-Teleports.patch} | 2 +- ...nfigurable-LibraryLoader-maven-repos.patch | 86 ++++++++++++++ ...patch => 0010-Leaves-Replay-Mod-API.patch} | 0 ... => 0011-Async-structure-locate-api.patch} | 0 ...> 0012-PlayerInventoryOverflowEvent.patch} | 0 ...3-Raytrace-AntiXray-SDK-integration.patch} | 0 12 files changed, 199 insertions(+), 79 deletions(-) create mode 100644 leaf-api/paper-patches/features/0003-Paper-Avoid-and-discourage-use-of-Maven-Central-as-a-CDN.patch rename leaf-api/paper-patches/features/{0003-Pufferfish-Sentry.patch => 0004-Pufferfish-Sentry.patch} (100%) rename leaf-api/paper-patches/features/{0004-Purpur-API-Changes.patch => 0005-Purpur-API-Changes.patch} (100%) rename leaf-api/paper-patches/features/{0005-Remove-Timings.patch => 0006-Remove-Timings.patch} (100%) rename leaf-api/paper-patches/features/{0006-KeYi-Player-Skull-API.patch => 0007-KeYi-Player-Skull-API.patch} (100%) delete mode 100644 leaf-api/paper-patches/features/0008-Configurable-LibraryLoader-maven-repos.patch rename leaf-api/paper-patches/features/{0007-Slice-Smooth-Teleports.patch => 0008-Slice-Smooth-Teleports.patch} (95%) create mode 100644 leaf-api/paper-patches/features/0009-Configurable-LibraryLoader-maven-repos.patch rename leaf-api/paper-patches/features/{0009-Leaves-Replay-Mod-API.patch => 0010-Leaves-Replay-Mod-API.patch} (100%) rename leaf-api/paper-patches/features/{0010-Async-structure-locate-api.patch => 0011-Async-structure-locate-api.patch} (100%) rename leaf-api/paper-patches/features/{0011-PlayerInventoryOverflowEvent.patch => 0012-PlayerInventoryOverflowEvent.patch} (100%) rename leaf-api/paper-patches/features/{0012-Raytrace-AntiXray-SDK-integration.patch => 0013-Raytrace-AntiXray-SDK-integration.patch} (100%) diff --git a/leaf-api/paper-patches/features/0003-Paper-Avoid-and-discourage-use-of-Maven-Central-as-a-CDN.patch b/leaf-api/paper-patches/features/0003-Paper-Avoid-and-discourage-use-of-Maven-Central-as-a-CDN.patch new file mode 100644 index 00000000..b0f3d5f7 --- /dev/null +++ b/leaf-api/paper-patches/features/0003-Paper-Avoid-and-discourage-use-of-Maven-Central-as-a-CDN.patch @@ -0,0 +1,112 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> +Date: Wed, 18 Jun 2025 10:47:21 -0700 +Subject: [PATCH] Paper: Avoid and discourage use of Maven Central as a CDN + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/62b7f86dae659deb2fc450285452d7c1439f92dc + +Default LibraryLoader to Google's Maven Central mirror, add MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR, and warn on use of Maven Central with MavenLibraryResolver + +https://www.sonatype.com/blog/maven-central-and-the-tragedy-of-the-commons +https://www.sonatype.com/blog/beyond-ips-addressing-organizational-overconsumption-in-maven-central + +diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java +index 107705db2d82b7c191e5e625ec888e0bc3b03831..ebb52c2c8d5fe8ca25513aadae8168180a3d426e 100644 +--- a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java ++++ b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java +@@ -41,7 +41,7 @@ import org.slf4j.LoggerFactory; + * MavenLibraryResolver resolver = new MavenLibraryResolver(); + * resolver.addDependency(new Dependency(new DefaultArtifact("org.jooq:jooq:3.17.7"), null)); + * resolver.addRepository(new RemoteRepository.Builder( +- * "central", "default", "https://repo1.maven.org/maven2/" ++ * "central", "default", MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR // Paper - Avoid and discourage use of Maven Central as a CDN + * ).build()); + * } + *

+@@ -50,6 +50,24 @@ import org.slf4j.LoggerFactory; + @NullMarked + public class MavenLibraryResolver implements ClassPathLibrary { + ++ // Paper start - Avoid and discourage use of Maven Central as a CDN ++ /** ++ * The default Maven Central mirror, configurable through the {@code PAPER_DEFAULT_CENTRAL_REPOSITORY} environment ++ * variable. Use this instead of Maven Central directly when you do not have your own mirror, as using ++ * Maven Central as a CDN is against the Maven Central Terms of Service, and you will cause users to hit ++ * rate limits. ++ * ++ *

This repository is also used by the legacy {@link org.bukkit.plugin.java.LibraryLoader}.

++ */ ++ public static final String MAVEN_CENTRAL_DEFAULT_MIRROR = getDefaultMavenCentralMirror(); ++ private static final List MAVEN_CENTRAL_URLS = List.of( ++ "https://repo1.maven.org/maven2", ++ "http://repo1.maven.org/maven2", ++ "https://repo.maven.apache.org/maven2", ++ "http://repo.maven.apache.org/maven2" ++ ); ++ // Paper end - Avoid and discourage use of Maven Central as a CDN ++ + private static final Logger LOGGER = LoggerFactory.getLogger("MavenLibraryResolver"); + + private final RepositorySystem repository; +@@ -105,6 +123,14 @@ public class MavenLibraryResolver implements ClassPathLibrary { + * dependencies from + */ + public void addRepository(final RemoteRepository remoteRepository) { ++ // Paper start - Avoid and discourage use of Maven Central as a CDN ++ if (MAVEN_CENTRAL_URLS.stream().anyMatch(remoteRepository.getUrl()::startsWith)) { ++ LOGGER.warn( ++ "Use of Maven Central as a CDN is against the Maven Central Terms of Service. Use MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR instead.", ++ new RuntimeException("Plugin used Maven Central for library resolution") ++ ); ++ } ++ // Paper end - Avoid and discourage use of Maven Central as a CDN + this.repositories.add(remoteRepository); + } + +@@ -130,4 +156,17 @@ public class MavenLibraryResolver implements ClassPathLibrary { + store.addLibrary(file.toPath()); + } + } ++ ++ // Paper start - Avoid and discourage use of Maven Central as a CDN ++ private static String getDefaultMavenCentralMirror() { ++ String central = System.getenv("PAPER_DEFAULT_CENTRAL_REPOSITORY"); ++ if (central == null) { ++ central = System.getProperty("org.bukkit.plugin.java.LibraryLoader.centralURL"); ++ } ++ if (central == null) { ++ central = "https://maven-central.storage-download.googleapis.com/maven2"; ++ } ++ return central; ++ } ++ // Paper end - Avoid and discourage use of Maven Central as a CDN + } +diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +index 211c093ce2253e918cd40725ebf1ef172d1b9bdf..998a5278f39d13ed6b0a6d03514658f28b0d420f 100644 +--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +@@ -49,6 +49,12 @@ public class LibraryLoader + public static java.util.function.BiFunction LIBRARY_LOADER_FACTORY; // Paper - rewrite reflection in libraries + public static java.util.function.Function, List> REMAPPER; // Paper - remap libraries + ++ // Paper start - Avoid and discourage use of Maven Central as a CDN ++ private static List getRepositories() { ++ return List.of(new RemoteRepository.Builder("central", "default", io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR).build()); ++ } ++ // Paper end - Avoid and discourage use of Maven Central as a CDN ++ + public LibraryLoader(@NotNull Logger logger) + { + this.logger = logger; +@@ -79,7 +85,7 @@ public class LibraryLoader + session.setSystemProperties( System.getProperties() ); + session.setReadOnly(); + +- this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) ); ++ this.repositories = repository.newResolutionRepositories( session, getRepositories()); // Paper - Avoid and discourage use of Maven Central as a CDN + } + + @Nullable diff --git a/leaf-api/paper-patches/features/0003-Pufferfish-Sentry.patch b/leaf-api/paper-patches/features/0004-Pufferfish-Sentry.patch similarity index 100% rename from leaf-api/paper-patches/features/0003-Pufferfish-Sentry.patch rename to leaf-api/paper-patches/features/0004-Pufferfish-Sentry.patch diff --git a/leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch b/leaf-api/paper-patches/features/0005-Purpur-API-Changes.patch similarity index 100% rename from leaf-api/paper-patches/features/0004-Purpur-API-Changes.patch rename to leaf-api/paper-patches/features/0005-Purpur-API-Changes.patch diff --git a/leaf-api/paper-patches/features/0005-Remove-Timings.patch b/leaf-api/paper-patches/features/0006-Remove-Timings.patch similarity index 100% rename from leaf-api/paper-patches/features/0005-Remove-Timings.patch rename to leaf-api/paper-patches/features/0006-Remove-Timings.patch diff --git a/leaf-api/paper-patches/features/0006-KeYi-Player-Skull-API.patch b/leaf-api/paper-patches/features/0007-KeYi-Player-Skull-API.patch similarity index 100% rename from leaf-api/paper-patches/features/0006-KeYi-Player-Skull-API.patch rename to leaf-api/paper-patches/features/0007-KeYi-Player-Skull-API.patch diff --git a/leaf-api/paper-patches/features/0008-Configurable-LibraryLoader-maven-repos.patch b/leaf-api/paper-patches/features/0008-Configurable-LibraryLoader-maven-repos.patch deleted file mode 100644 index 2568be33..00000000 --- a/leaf-api/paper-patches/features/0008-Configurable-LibraryLoader-maven-repos.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Sat, 3 Feb 2024 18:45:53 -0500 -Subject: [PATCH] Configurable LibraryLoader maven repos - -TODO - Dreeam: Support multi maven repos for lib downloading. - -Add JVM flag `-DLeaf.library-download-repo=link` to choose library download repo link. -e.g. `-DLeaf.library-download-repo=https://maven.aliyun.com/repository/public` - -diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java -index 107705db2d82b7c191e5e625ec888e0bc3b03831..77a58fc7c173b1724d44b0eeaf23b4a1b22b5fcb 100644 ---- a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java -+++ b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java -@@ -105,7 +105,7 @@ public class MavenLibraryResolver implements ClassPathLibrary { - * dependencies from - */ - public void addRepository(final RemoteRepository remoteRepository) { -- this.repositories.add(remoteRepository); -+ this.repositories.add(org.dreeam.leaf.plugin.loader.MavenCentralMirror.getCentralRepo(remoteRepository)); // Leaf - Configurable LibraryLoader maven repos - } - - /** -diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -index 211c093ce2253e918cd40725ebf1ef172d1b9bdf..096140a91f19eb31a10631b949f4402e37e1c601 100644 ---- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -@@ -79,7 +79,19 @@ public class LibraryLoader - session.setSystemProperties( System.getProperties() ); - session.setReadOnly(); - -- this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) ); -+ // Leaf start - Configurable LibraryLoader maven repos -+ this.repositories = repository.newResolutionRepositories( -+ session, -+ List.of(org.dreeam.leaf.plugin.loader.MavenCentralMirror.getCentralRepo("https://repo.maven.apache.org/maven2")) -+ ); -+ /* // Dreeam TODO -+ this.repositories = repository.newResolutionRepositories(session, List.of( -+ new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2").build(), -+ new RemoteRepository.Builder("aliyun", "default", "https://maven.aliyun.com/repository/public").build(), -+ new RemoteRepository.Builder("tencentclound", "default", "https://mirrors.cloud.tencent.com/nexus/repository/maven-public/").build(), -+ new RemoteRepository.Builder("huaweicloud", "default", "https://repo.huaweicloud.com/repository/maven/").build() -+ ));*/ -+ // Leaf end - Configurable LibraryLoader maven repos - } - - @Nullable -diff --git a/src/main/java/org/dreeam/leaf/plugin/loader/MavenCentralMirror.java b/src/main/java/org/dreeam/leaf/plugin/loader/MavenCentralMirror.java -new file mode 100644 -index 0000000000000000000000000000000000000000..95534cb6d771a0fe288e7c843c41b0036bdc7095 ---- /dev/null -+++ b/src/main/java/org/dreeam/leaf/plugin/loader/MavenCentralMirror.java -@@ -0,0 +1,24 @@ -+package org.dreeam.leaf.plugin.loader; -+ -+import org.eclipse.aether.repository.RemoteRepository; -+ -+public class MavenCentralMirror { -+ -+ public static final String MAVEN_CENTRAL_MIRROR_REPO = System.getProperty("Leaf.library-download-repo"); -+ -+ public static RemoteRepository getCentralRepo(RemoteRepository repo) { -+ if (MAVEN_CENTRAL_MIRROR_REPO != null && repo.getUrl().contains("repo.maven.apache.org/maven2")) { -+ repo = new RemoteRepository.Builder("central", "default", MAVEN_CENTRAL_MIRROR_REPO).build(); -+ } -+ -+ return repo; -+ } -+ -+ public static RemoteRepository getCentralRepo(String repo) { -+ if (MAVEN_CENTRAL_MIRROR_REPO != null) { -+ repo = MAVEN_CENTRAL_MIRROR_REPO; -+ } -+ -+ return new RemoteRepository.Builder("central", "default", repo).build(); -+ } -+} diff --git a/leaf-api/paper-patches/features/0007-Slice-Smooth-Teleports.patch b/leaf-api/paper-patches/features/0008-Slice-Smooth-Teleports.patch similarity index 95% rename from leaf-api/paper-patches/features/0007-Slice-Smooth-Teleports.patch rename to leaf-api/paper-patches/features/0008-Slice-Smooth-Teleports.patch index 651e7397..c75b4a33 100644 --- a/leaf-api/paper-patches/features/0007-Slice-Smooth-Teleports.patch +++ b/leaf-api/paper-patches/features/0008-Slice-Smooth-Teleports.patch @@ -9,7 +9,7 @@ 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/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 2a8abee51e8fad62b0aa58a47eadfbac2bf51fdf..716f99b4d09ffd415f8a53a90031c92d8931f125 100644 +index 69f7a2fa54b667f2bb97454d6be9f6322a9aa43d..c92e0bb60504900fa1d1e7c86534a46578c91799 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -3709,6 +3709,33 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM diff --git a/leaf-api/paper-patches/features/0009-Configurable-LibraryLoader-maven-repos.patch b/leaf-api/paper-patches/features/0009-Configurable-LibraryLoader-maven-repos.patch new file mode 100644 index 00000000..151b87cc --- /dev/null +++ b/leaf-api/paper-patches/features/0009-Configurable-LibraryLoader-maven-repos.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Sat, 3 Feb 2024 18:45:53 -0500 +Subject: [PATCH] Configurable LibraryLoader maven repos + +Add JVM flag `-DLeaf.library-download-repo=link` to choose library download repo link. +e.g. `-DLeaf.library-download-repo=https://maven.aliyun.com/repository/public` + +diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java +index ebb52c2c8d5fe8ca25513aadae8168180a3d426e..006a86da6afa1b7b80df5df073ebd236e27cd2b5 100644 +--- a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java ++++ b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java +@@ -60,12 +60,16 @@ public class MavenLibraryResolver implements ClassPathLibrary { + *

This repository is also used by the legacy {@link org.bukkit.plugin.java.LibraryLoader}.

+ */ + public static final String MAVEN_CENTRAL_DEFAULT_MIRROR = getDefaultMavenCentralMirror(); +- private static final List MAVEN_CENTRAL_URLS = List.of( ++ // Leaf start - Configurable LibraryLoader maven repos ++ @org.jspecify.annotations.Nullable ++ public static final RemoteRepository MAVEN_CENTRAL_MIRROR_REPO = getCentralMirrorRepo(); ++ private static final String[] MAVEN_CENTRAL_URLS = new String[]{ + "https://repo1.maven.org/maven2", + "http://repo1.maven.org/maven2", + "https://repo.maven.apache.org/maven2", + "http://repo.maven.apache.org/maven2" +- ); ++ }; ++ // Leaf end - Configurable LibraryLoader maven repos + // Paper end - Avoid and discourage use of Maven Central as a CDN + + private static final Logger LOGGER = LoggerFactory.getLogger("MavenLibraryResolver"); +@@ -124,12 +128,21 @@ public class MavenLibraryResolver implements ClassPathLibrary { + */ + public void addRepository(final RemoteRepository remoteRepository) { + // Paper start - Avoid and discourage use of Maven Central as a CDN +- if (MAVEN_CENTRAL_URLS.stream().anyMatch(remoteRepository.getUrl()::startsWith)) { +- LOGGER.warn( +- "Use of Maven Central as a CDN is against the Maven Central Terms of Service. Use MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR instead.", +- new RuntimeException("Plugin used Maven Central for library resolution") +- ); ++ // Leaf start - Configurable LibraryLoader maven repos ++ for (String url : MAVEN_CENTRAL_URLS) { ++ if (remoteRepository.getUrl().startsWith(url)) { ++ RemoteRepository mirrorRepo = MAVEN_CENTRAL_MIRROR_REPO; ++ if (mirrorRepo != null) { ++ this.repositories.add(mirrorRepo); ++ return; ++ } ++ LOGGER.warn( ++ "Use of Maven Central as a CDN is against the Maven Central Terms of Service. Use MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR instead.", ++ new RuntimeException("Plugin used Maven Central for library resolution") ++ ); ++ } + } ++ // Leaf end - Configurable LibraryLoader maven repos + // Paper end - Avoid and discourage use of Maven Central as a CDN + this.repositories.add(remoteRepository); + } +@@ -169,4 +182,15 @@ public class MavenLibraryResolver implements ClassPathLibrary { + return central; + } + // Paper end - Avoid and discourage use of Maven Central as a CDN ++ ++ // Leaf start - Configurable LibraryLoader maven repos ++ @org.jspecify.annotations.Nullable ++ private static RemoteRepository getCentralMirrorRepo() { ++ String mirrorAddr = System.getProperty("Leaf.library-download-repo"); ++ if (mirrorAddr != null) { ++ new RemoteRepository.Builder("central", "default", mirrorAddr).build(); ++ } ++ return null; ++ } ++ // Leaf end - Configurable LibraryLoader maven repos + } +diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +index 998a5278f39d13ed6b0a6d03514658f28b0d420f..2b4f6f305d3e4418b912e36a6b603c412111947f 100644 +--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +@@ -51,6 +51,7 @@ public class LibraryLoader + + // Paper start - Avoid and discourage use of Maven Central as a CDN + private static List getRepositories() { ++ if (io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver.MAVEN_CENTRAL_MIRROR_REPO != null) return List.of(io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver.MAVEN_CENTRAL_MIRROR_REPO); // Leaf - Configurable LibraryLoader maven repos + return List.of(new RemoteRepository.Builder("central", "default", io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR).build()); + } + // Paper end - Avoid and discourage use of Maven Central as a CDN diff --git a/leaf-api/paper-patches/features/0009-Leaves-Replay-Mod-API.patch b/leaf-api/paper-patches/features/0010-Leaves-Replay-Mod-API.patch similarity index 100% rename from leaf-api/paper-patches/features/0009-Leaves-Replay-Mod-API.patch rename to leaf-api/paper-patches/features/0010-Leaves-Replay-Mod-API.patch diff --git a/leaf-api/paper-patches/features/0010-Async-structure-locate-api.patch b/leaf-api/paper-patches/features/0011-Async-structure-locate-api.patch similarity index 100% rename from leaf-api/paper-patches/features/0010-Async-structure-locate-api.patch rename to leaf-api/paper-patches/features/0011-Async-structure-locate-api.patch diff --git a/leaf-api/paper-patches/features/0011-PlayerInventoryOverflowEvent.patch b/leaf-api/paper-patches/features/0012-PlayerInventoryOverflowEvent.patch similarity index 100% rename from leaf-api/paper-patches/features/0011-PlayerInventoryOverflowEvent.patch rename to leaf-api/paper-patches/features/0012-PlayerInventoryOverflowEvent.patch diff --git a/leaf-api/paper-patches/features/0012-Raytrace-AntiXray-SDK-integration.patch b/leaf-api/paper-patches/features/0013-Raytrace-AntiXray-SDK-integration.patch similarity index 100% rename from leaf-api/paper-patches/features/0012-Raytrace-AntiXray-SDK-integration.patch rename to leaf-api/paper-patches/features/0013-Raytrace-AntiXray-SDK-integration.patch From 60539427ff72517363c6df3e9640d44a9ecc5ea2 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:15:03 +0800 Subject: [PATCH 8/8] Cleanup patch --- ... => 0189-Paper-Fix-excess-slot-updates.patch} | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) rename leaf-server/minecraft-patches/features/{0189-Paper-Fix-excess-slot-updates-inventory-state-id-des.patch => 0189-Paper-Fix-excess-slot-updates.patch} (67%) diff --git a/leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates-inventory-state-id-des.patch b/leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates.patch similarity index 67% rename from leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates-inventory-state-id-des.patch rename to leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates.patch index d921c5df..cce1462f 100644 --- a/leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates-inventory-state-id-des.patch +++ b/leaf-server/minecraft-patches/features/0189-Paper-Fix-excess-slot-updates.patch @@ -1,12 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Wed, 11 Jun 2025 20:51:36 +0200 -Subject: [PATCH] Paper: Fix excess slot updates / inventory state id desync +From: AJ Ferguson +Date: Mon, 6 Jan 2025 20:31:00 +1100 +Subject: [PATCH] Paper: Fix excess slot updates -Original Patch: https://github.com/PaperMC/Paper/pull/12654 +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper +Paper pull request: https://github.com/PaperMC/Paper/pull/12654 + +https://github.com/PaperMC/Paper/commit/e714de636543d61fcd3682705484c8a15f4f3ca6 diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index ff2ff95ec9d94e2e31e8174196b384c37d56f38a..2a49a0bdeb61c4fadddc241c8ebca908959d7e9c 100644 +index ff2ff95ec9d94e2e31e8174196b384c37d56f38a..3ffc454609adc82c6a4daba68efac129bee38eb5 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -553,7 +553,7 @@ public abstract class AbstractContainerMenu { @@ -14,7 +18,7 @@ index ff2ff95ec9d94e2e31e8174196b384c37d56f38a..2a49a0bdeb61c4fadddc241c8ebca908 slot.setChanged(); // CraftBukkit start - Make sure the client has the right slot contents - if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != 64) { -+ if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != net.minecraft.world.Container.MAX_STACK) { ++ if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != Container.MAX_STACK) { // Paper - craftbukkkit - Fix excess slot updates serverPlayer.connection.send(new ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), slot.index, slot.getItem())); // Updating a crafting inventory makes the client reset the result slot, have to send it again if (this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.WORKBENCH || this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.CRAFTING) {