diff --git a/.github/workflows/build-1215.yml b/.github/workflows/build-1215.yml index 660e3fd2..65bdfc35 100644 --- a/.github/workflows/build-1215.yml +++ b/.github/workflows/build-1215.yml @@ -97,7 +97,7 @@ jobs: path: ./leaf-1.21.5-${{ env.BUILD_NUMBER }}.jar - name: Release Leaf - uses: softprops/action-gh-release@master + uses: softprops/action-gh-release@v2.2.2 # Temp fix with: name: "Leaf 1.21.5" tag_name: "ver-1.21.5" diff --git a/.gitignore b/.gitignore index 136dc0db..811e2614 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ # IntelliJ *.iml .idea +!.idea/icon.svg # Gradle !gradle/wrapper/gradle-wrapper.jar @@ -19,7 +20,7 @@ build # Leaf build-data/dev-imports.txt -patches/todo +leaf-archived-patches/todo run leaf-api/build.gradle.kts diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 00000000..e4563310 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 8156cd5e..86cae39d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,32 @@ Leaf
-[![Github Releases](https://img.shields.io/badge/Download-Releases-blue?&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/releases)⠀ +[![Download](https://img.shields.io/badge/releases-blue?label=download&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/download)⠀ [![Github Actions Build](https://img.shields.io/github/actions/workflow/status/Winds-Studio/Leaf/build-1215.yml?&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/actions)⠀ [![Discord](https://img.shields.io/discord/1145991395388162119?label=discord&style=for-the-badge&colorA=19201a&colorB=298046)](https://discord.gg/gfgAwdSEuM) -[![Docs](https://img.shields.io/badge/Docs-leafmc.one/docs/-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/docs/) +[![Docs](https://img.shields.io/badge/leafmc.one/docs/-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/docs) -**Leaf** is a [Paper](https://papermc.io/) fork designed to be customized and high-performance. +**Leaf** is a [Paper](https://papermc.io/) fork designed to be customizable and high-performance.
-> [!WARNING] -> Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute by optimizing or reporting issues. +> [!WARNING] +> Leaf is a performance-oriented fork. Make sure to take backups **before** switching to it. Everyone is welcome to contribute optimizations or report issues to help us improve. + +**English** | [中文](public/readme/README_CN.md) ## 🍃 Features - - **Based on [Paper](https://papermc.io/)** for generic performance and flexible API - - **Async** pathfinding, mob spawning and entity tracker - - **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits) and our own - - **Fully compatible** with Bukkit, Spigot and Paper plugins - - **Latest dependencies**, keeping all dependencies in the newest version - - **Allows all characters in usernames**, including Chinese and other characters - - **Fixes** some Minecraft bugs - - **Mod Protocols** support - - **More customized** relying on features of [Purpur](https://github.com/PurpurMC/Purpur) - - Support for **Linear region file format** - - **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easy track all errors coming from your server in excruciating detail - - And more... +- **Based on [Paper](https://papermc.io/)** for generic performance and flexible API +- **Async** pathfinding, mob spawning and entity tracker +- **Various optimizations** blending from [other forks](#-credits) and our own +- **Fully compatible** with Spigot and Paper plugins +- **Latest dependencies**, keeping all dependencies up-to-date +- **Allows all characters in usernames**, including Chinese and other characters +- **Fixes** some Minecraft bugs +- **Mod Protocols** support +- **More customized** relying on features of [Purpur](https://github.com/PurpurMC/Purpur) +- **Linear region file format**, to save disk space +- **Maintenance friendly**, integrating with [Sentry](https://sentry.io/welcome/) of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) to easily track all errors coming from your server in extreme detail +- And more... ## 📈 bStats [![bStats Graph Data](https://bstats.org/signatures/server-implementation/Leaf.svg)](https://bstats.org/plugin/server-implementation/Leaf) @@ -37,12 +39,12 @@ If you love our work, feel free to donate via our [Open Collective](https://opencollective.com/Winds-Studio) or [Dreeam's AFDIAN](https://afdian.com/a/Dreeam) :) ## 📥 Download -You can find the latest successful build in [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) or [Releases](https://github.com/Winds-Studio/Leaf/releases) +Download Leaf from our [website](https://www.leafmc.one/download) or get latest build in [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) **Please note Java >= 21 is required.** ## 📄 Documentation -Documentation on how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs) +Documentation about how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs) ## 📦 Building Building a Paperclip JAR for distribution: @@ -89,11 +91,11 @@ Paperweight files are licensed under [MIT](licenses/MIT.txt). Patches are licensed under [MIT](licenses/MIT.txt), unless indicated differently in their header. Binaries are licensed under [GPL-3.0](licenses/GPL-3.0.txt). -Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/Paperweight](https://github.com/PaperMC/paperweight) for the license of some material used by this project. +Also see [PaperMC/Paper](https://github.com/PaperMC/Paper) and [PaperMC/paperweight](https://github.com/PaperMC/paperweight) for the licenses of some materials used by this project. ## 📜 Credits Thanks to these projects below. Leaf includes some patches taken from them.
-If these excellent projects hadn't appeared, Leaf wouldn't have become great. +If these excellent projects hadn't existed, Leaf wouldn't have become great. - [Gale](https://github.com/Dreeam-qwq/Gale) ([Original Repo](https://github.com/GaleMC/Gale)) - [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) @@ -126,9 +128,9 @@ If these excellent projects hadn't appeared, Leaf wouldn't have become great. Jianke Cloud Host cloud of swordsman | 剑客云 -If you want to find a cheaper, high performance, stable with lower latency, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). +If you want to find a cheaper, high performance, stable, lower latency host, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). -如果你想找一个低价高性能, 低延迟的云服务商,剑客云是个不错的选择! 你可以在[这里](https://cloud.swordsman.com.cn/?i8ab42c)注册. +如果你想找一个低价高性能、低延迟的云服务商,剑客云是个不错的选择!你可以在 [这里](https://cloud.swordsman.com.cn/?i8ab42c) 注册。 --- ![YourKit](https://www.yourkit.com/images/yklogo.png) diff --git a/build-data/leaf.at b/build-data/leaf.at index 34938c58..a9eaa7b1 100644 --- a/build-data/leaf.at +++ b/build-data/leaf.at @@ -4,6 +4,8 @@ protected net.minecraft.world.entity.Entity dimensions protected net.minecraft.world.entity.ai.goal.RemoveBlockGoal blockToRemove protected net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal getTargetConditions()Lnet/minecraft/world/entity/ai/targeting/TargetingConditions; protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape +public net.minecraft.core.Direction VALUES +public net.minecraft.server.level.ServerChunkCache fullChunks public net.minecraft.server.level.ServerEntity sendDirtyEntityData()V public net.minecraft.server.players.PlayerList SEND_PLAYER_INFO_INTERVAL public net.minecraft.util.Mth SIN diff --git a/gradle.properties b/gradle.properties index 1514320d..310e1171 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,3 @@ org.gradle.configuration-cache=true org.gradle.caching=true org.gradle.parallel=true org.gradle.vfs.watch=false -leaf.patcher.repo-cache=enabled -leaf.patcher.patch-cache=enabled -leaf.patcher.fast-update=true diff --git a/leaf-api/build.gradle.kts.patch b/leaf-api/build.gradle.kts.patch index f2a00386..9dd03e4e 100644 --- a/leaf-api/build.gradle.kts.patch +++ b/leaf-api/build.gradle.kts.patch @@ -33,7 +33,7 @@ api("org.apache.logging.log4j:log4j-api:$log4jVersion") api("org.slf4j:slf4j-api:$slf4jVersion") api("com.mojang:brigadier:1.3.10") -+ api("io.sentry:sentry:8.4.0") // Pufferfish ++ api("io.sentry:sentry:8.13.2") // Pufferfish // Deprecate bungeecord-chat in favor of adventure api("net.md-5:bungeecord-chat:$bungeeCordChatVersion") { diff --git a/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java b/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java new file mode 100644 index 00000000..510a54ac --- /dev/null +++ b/leaf-api/src/main/java/org/dreeam/leaf/event/BlockExplosionHitEvent.java @@ -0,0 +1,63 @@ +package org.dreeam.leaf.event; + +import org.bukkit.ExplosionResult; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.block.BlockEvent; +import org.jetbrains.annotations.NotNull; + +/** + * Called when a block executes its explosion hit actions. + * If the event is cancelled, the block will not execute the explosion hit actions. + */ +public class BlockExplosionHitEvent extends BlockEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final Entity source; + private final ExplosionResult result; + + public BlockExplosionHitEvent(@NotNull Block block, Entity source, ExplosionResult result) { + super(block); + this.source = source; + this.result = result; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + /** + * Returns the entity responsible for the explosion. + * + * @return Entity responsible for the explosion + */ + public Entity getSource() { + return source; + } + + /** + * Returns the result of the explosion. + * + * @return the result of the explosion + */ + public ExplosionResult getResult() { + return result; + } +} diff --git a/leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch b/leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch deleted file mode 100644 index 049a33d3..00000000 --- a/leaf-archived-patches/removed/hardfork/paperserver/0002-Leaf-Bootstrap.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Wed, 31 Jul 2024 22:05:21 +0800 -Subject: [PATCH] Leaf Bootstrap - -Removed since Leaf 1.21.4, useless - -org.bukkit.craftbukkit.Main#main -> LeafBootstrap -> PaperBootstrap -> ... - -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index ecb0fcd1f3b3f3d7751eded3cdf0977c1889c9ed..d0becb56a9911ef4cc55ae8d7c47832f442ad52f 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -278,7 +278,8 @@ public class Main { - System.setProperty("jdk.console", "java.base"); // Paper - revert default console provider back to java.base so we can have our own jline - //System.out.println("Loading libraries, please wait..."); - //net.minecraft.server.Main.main(options); -- io.papermc.paper.PaperBootstrap.boot(options); -+ //io.papermc.paper.PaperBootstrap.boot(options); // Leaf - Leaf Boostrap - diff on change -+ org.dreeam.leaf.LeafBootstrap.boot(options); // Leaf - Leaf Boostrap - } catch (Throwable t) { - t.printStackTrace(); - } diff --git a/leaf-server/minecraft-patches/features/0190-Smooth-teleport-config.patch b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch similarity index 90% rename from leaf-server/minecraft-patches/features/0190-Smooth-teleport-config.patch rename to leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch index f0e38705..30429f8d 100644 --- a/leaf-server/minecraft-patches/features/0190-Smooth-teleport-config.patch +++ b/leaf-archived-patches/removed/hardfork/server/0100-Smooth-teleport-config.patch @@ -3,16 +3,18 @@ From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Tue, 9 Nov 2077 00:00:00 +0800 Subject: [PATCH] Smooth teleport config +Removed since Leaf 1.21.4 + This abuses some of how Minecraft works and attempts to teleport a player to another world without triggering typical respawn packets. All of natural state of chunk resends, entity adds/removes, etc still happen but the visual "refresh" of a world change is hidden. Depending on the destination location/world, this can act as a "smooth teleport" to a world if the new world is very similar looking to the old one. diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 18125ed336c3425f123232b405a8af9ee3a2ba7d..e97a93c97e1822f969dce2d30dd915db5d3d14cf 100644 +index 4f01b53bf801f99253efd27df6216912705d18af..82a1715fea41e6a41c4ff441ea89f424f68ba190 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1419,6 +1419,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1469,6 +1469,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc LevelData worlddata = level.getLevelData(); this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(level), (byte) 3)); @@ -20,9 +22,9 @@ index 18125ed336c3425f123232b405a8af9ee3a2ba7d..e97a93c97e1822f969dce2d30dd915db this.connection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); PlayerList playerList = this.server.getPlayerList(); -@@ -1428,7 +1429,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1478,7 +1479,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc // CraftBukkit end - this.portalPos = org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(exit); // Purpur - Fix stuck in portals + this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur - Fix stuck in portals this.setServerLevel(level); - this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); // CraftBukkit - use internal teleport without event + if (!org.dreeam.leaf.config.modules.gameplay.SmoothTeleport.enabled || !PlayerList.isSameLogicalHeight(serverLevel, level)) this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); // CraftBukkit - use internal teleport without event // Leaf - Smooth teleport @@ -30,10 +32,10 @@ index 18125ed336c3425f123232b405a8af9ee3a2ba7d..e97a93c97e1822f969dce2d30dd915db level.addDuringTeleport(this); this.triggerDimensionChangeTriggers(serverLevel); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 34c5c2e3d6b0706cb91ec0171969cf81e94741a4..8b1652805a2ac9ebae9c99dd77e81c7a484a7abb 100644 +index 75fb49f1596f475278d12c8c7aea9ad4952b6056..b17c8a2f5294ac28cc05fb05c84a041b2c6c8721 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -956,11 +956,11 @@ public abstract class PlayerList { +@@ -955,11 +955,11 @@ public abstract class PlayerList { byte b = (byte)(keepInventory ? 1 : 0); ServerLevel serverLevel = serverPlayer.serverLevel(); LevelData levelData = serverLevel.getLevelData(); diff --git a/leaf-server/minecraft-patches/features/0192-Collect-then-startEachNonRunningBehavior-in-Brain.patch b/leaf-archived-patches/removed/hardfork/server/0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch similarity index 90% rename from leaf-server/minecraft-patches/features/0192-Collect-then-startEachNonRunningBehavior-in-Brain.patch rename to leaf-archived-patches/removed/hardfork/server/0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch index a8155086..5909a99d 100644 --- a/leaf-server/minecraft-patches/features/0192-Collect-then-startEachNonRunningBehavior-in-Brain.patch +++ b/leaf-archived-patches/removed/hardfork/server/0101-Collect-then-startEachNonRunningBehavior-in-Brain.patch @@ -3,12 +3,13 @@ From: Taiyou06 Date: Tue, 9 Nov 2077 00:00:00 +0800 Subject: [PATCH] Collect then startEachNonRunningBehavior in Brain +Removed since Leaf 1.21.4, replaced by Cache potential behaviors in Brain patch diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index b946faf899561b9e4f8399e16600eede54bb55a5..b2e17d387558875f45a8458cf874a5b056820432 100644 +index c561b749fb9b76ba9b1e9689089b743248c65d50..ea6c8e85ccff67b1c24109732f74f1e8199cad07 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java -@@ -462,20 +462,29 @@ public class Brain { +@@ -453,20 +453,29 @@ public class Brain { } private void startEachNonRunningBehavior(ServerLevel level, E entity) { diff --git a/leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch b/leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch similarity index 97% rename from leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch rename to leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch index 55d9d72f..093e5879 100644 --- a/leaf-archived-patches/work/server/0110-Cache-ItemStack-max-stack-size.patch +++ b/leaf-archived-patches/removed/hardfork/server/0110-Cache-ItemStack-max-stack-size.patch @@ -3,7 +3,7 @@ From: Taiyou06 Date: Fri, 7 Feb 2025 21:41:51 +0100 Subject: [PATCH] Cache ItemStack max stack size -TODO: check this, fix get max stack size +Abandoned plan, maybe research deeper in this part in the future, maybe not diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java index fd7c1e800cbd4919a1a47f6c468c8776535bd028..fdd7e89bacc98e23f067ba17d0bd93ee84a388cb 100644 diff --git a/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch b/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch deleted file mode 100644 index 6a9bc4c4..00000000 --- a/leaf-archived-patches/work/server/0042-Optimize-Minecart-collisions.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Sat, 6 Apr 2024 22:57:41 -0400 -Subject: [PATCH] Optimize Minecart collisions - -Co-authored-by: MrHua269 - -Skip tick collisions to to prevent lag causing by massive stacked Minecart -Useful for anarchy server. - -diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java -index 4830ebddade00f62287bcc9d7b17be83c0ad3a56..fca917561944017e032ea39ffb22cbd2c89b9f51 100644 ---- a/src/main/java/net/minecraft/world/entity/EntitySelector.java -+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java -@@ -63,6 +63,13 @@ public final class EntitySelector { - } - public static Predicate pushable(Entity entity, boolean ignoreClimbing) { - // Paper end - Climbing should not bypass cramming gamerule -+ -+ // Leaf start - Optimize Minecart collisions -+ if (entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) { -+ return x -> true; -+ } -+ // Leaf end - Optimize Minecart collisions -+ - PlayerTeam scoreboardteam = entity.getTeam(); - Team.CollisionRule scoreboardteambase_enumteampush = scoreboardteam == null ? Team.CollisionRule.ALWAYS : scoreboardteam.getCollisionRule(); - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -index 8b1c00062a5272d1020bc85491d8627c4d5f46cb..ec437f625f10098c008571569abb89ad4af52781 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -@@ -386,15 +386,15 @@ public abstract class AbstractMinecart extends VehicleEntity { - this.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); - } - // CraftBukkit end -- if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) { -+ // Leaf start - Optimize Minecart collision handling -+ // The logic below is used to get list of entities around Minecart -+ // and handle behaviors for their collisions with each other -+ if (!org.dreeam.leaf.config.modules.opt.OptimizeMinecart.enabled || this.tickCount % org.dreeam.leaf.config.modules.opt.OptimizeMinecart.skipTickCount == 0) { -+ if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && (org.dreeam.leaf.config.modules.opt.OptimizeMinecart.enabled || this.getDeltaMovement().horizontalDistanceSqr() > 0.01D)) { - List list = this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this)); - - if (!list.isEmpty()) { -- Iterator iterator = list.iterator(); -- -- while (iterator.hasNext()) { -- Entity entity = (Entity) iterator.next(); -- -+ for (Entity entity : list) { - if (!(entity instanceof Player) && !(entity instanceof IronGolem) && !(entity instanceof AbstractMinecart) && !this.isVehicle() && !entity.isPassenger()) { - // CraftBukkit start - VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity()); -@@ -421,11 +421,7 @@ public abstract class AbstractMinecart extends VehicleEntity { - } - } - } else { -- Iterator iterator1 = this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); -- -- while (iterator1.hasNext()) { -- Entity entity1 = (Entity) iterator1.next(); -- -+ for (Entity entity1 : this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D))) { - if (!this.hasPassenger(entity1) && entity1.isPushable() && entity1 instanceof AbstractMinecart) { - // CraftBukkit start - VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity1.getBukkitEntity()); -@@ -439,6 +435,8 @@ public abstract class AbstractMinecart extends VehicleEntity { - } - } - } -+ } -+ // Leaf end - - this.updateInWaterStateAndDoFluidPushing(); - if (this.isInLava()) { -diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8ca03c5abfb9e11dfbb6e65453052780e289c686 ---- /dev/null -+++ b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java -@@ -0,0 +1,26 @@ -+package org.dreeam.leaf.config.modules.opt; -+ -+import org.dreeam.leaf.config.ConfigModules; -+import org.dreeam.leaf.config.EnumConfigCategory; -+ -+public class OptimizeMinecart extends ConfigModules { -+ -+ public String getBasePath() { -+ return EnumConfigCategory.PERF.getBaseKeyName() + ".optimize-minecart"; -+ } -+ -+ public static boolean enabled = false; -+ public static int skipTickCount = 30; -+ -+ @Override -+ public void onLoaded() { -+ enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config.pickStringRegionBased(""" -+ Enable this feature to handle a large amount of stacked minecarts better. -+ By skipping tick collisions to reduce expensive getEntities -+ and bukkit event calls, useful for anarchy servers. -+ """, -+ """ -+ 开启此项可更好地承受大量堆叠矿车. (通过跳过碰撞 tick 的方式)""")); -+ skipTickCount = config.getInt(getBasePath() + ".skip-tick-count", skipTickCount); -+ } -+} diff --git a/leaf-archived-patches/work/server/0181-optimize-mob-spawning.patch b/leaf-archived-patches/work/server/0181-optimize-mob-spawning.patch new file mode 100644 index 00000000..d606a09a --- /dev/null +++ b/leaf-archived-patches/work/server/0181-optimize-mob-spawning.patch @@ -0,0 +1,204 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:20:59 +0900 +Subject: [PATCH] optimize mob spawning + + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index f57f8e610dac80b8095bfc0c7e4b22ff5ad6b13c..c1efd558cfbfd2200295ef5755aa496e95deb7d7 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -70,7 +70,9 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + private final Set chunkHoldersToBroadcast = new ReferenceOpenHashSet<>(); + @Nullable + @VisibleForDebug +- private NaturalSpawner.SpawnState lastSpawnState; ++ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf - optimize mob spawning ++ private long delayTimeInhabited = 0L; // Leaf - optimize mob spawning ++ private long delaySpawn = -1L; // Leaf - optimize mob spawning + // Paper start + public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); + public int getFullChunksCount() { +@@ -655,13 +657,37 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + filteredSpawningCategories = List.of(); + } + +- for (LevelChunk levelChunk : chunks) { +- ChunkPos pos = levelChunk.getPos(); +- levelChunk.incrementInhabitedTime(timeInhabited); +- if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && lastSpawnState != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot // Pufferfish // Leaf - Don't spawn if lastSpawnState is null +- NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState, filteredSpawningCategories); // Pufferfish ++ // Leaf start - optimize mob spawning ++ var lastSpawnState1 = this.lastSpawnState; ++ if (lastSpawnState1 != null && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { ++ long sumTimeInhabited = timeInhabited + delayTimeInhabited; ++ long time = level.getGameTime(); ++ for (LevelChunk levelChunk : chunks) { ++ ChunkPos pos = levelChunk.getPos(); ++ levelChunk.incrementInhabitedTime(sumTimeInhabited); ++ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish ++ } ++ } ++ if (delaySpawn != -1L) { ++ time = delaySpawn; ++ for (LevelChunk levelChunk : chunks) { ++ ChunkPos pos = levelChunk.getPos(); ++ if (!filteredSpawningCategories.isEmpty() && this.level.getWorldBorder().isWithinBounds(pos) && this.chunkMap.anyPlayerCloseEnoughForSpawning(pos, true)) { // Spigot ++ NaturalSpawner.spawnForChunk(this.level, levelChunk, lastSpawnState1, filteredSpawningCategories, time); // Pufferfish ++ } ++ } + } ++ delaySpawn = -1L; ++ delayTimeInhabited = 0L; ++ } else { ++ // unlikely ++ delayTimeInhabited += timeInhabited; ++ delaySpawn = level.getGameTime(); ++ } ++ // Leaf end - optimize mob spawning + ++ for (LevelChunk levelChunk : chunks) { // Leaf - optimize mob spawning - split to 2 loop + if (true) { // Paper - rewrite chunk system + this.level.tickChunk(levelChunk, _int); + } +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index 9b37b763c6555705f3e256010f508b5a0c2cdb66..bde7008e14a3b4c0a37a94a4890e2f7fa1ce2466 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -155,7 +155,13 @@ public final class NaturalSpawner { + return list; + } + ++ @Deprecated(forRemoval = true) // Leaf - optimize mob spawning + public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories) { ++ // Leaf start - optimize mob spawning ++ spawnForChunk(level, chunk, spawnState, categories, level.getGameTime()); ++ } ++ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, List categories, long gameTime) { ++ // Leaf end - optimize mob spawning + for (MobCategory mobCategory : categories) { + // Paper start - Optional per player mob spawns + final boolean canSpawn; +@@ -174,7 +180,7 @@ public final class NaturalSpawner { + } + // Paper end - throttle failed spawn attempts + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { +- spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts ++ spawnThisTick = ticksPerSpawnTmp != 0 && gameTime % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts // Leaf - optimize mob spawning + limit = level.getWorld().getSpawnLimit(spawnCategory); + } + +@@ -238,12 +244,14 @@ public final class NaturalSpawner { + // Paper end - throttle failed spawn attempts + ) { + // Paper end - Optional per player mob spawns +- BlockPos randomPosWithin = getRandomPosWithin(level, chunk); +- if (randomPosWithin.getY() >= level.getMinY() + 1) { +- return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ // Leaf start - optimize mob spawning ++ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() < level.getMinY() + 1) { ++ return 0; + } +- +- return 0; // Paper - throttle failed spawn attempts ++ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ // Leaf end - optimize mob spawning + } + + @VisibleForDebug +@@ -275,31 +283,55 @@ public final class NaturalSpawner { + StructureManager structureManager = level.structureManager(); + ChunkGenerator generator = level.getChunkSource().getGenerator(); + int y = pos.getY(); ++ int posX = pos.getX(); // Leaf - optimize mob spawning ++ int posZ = pos.getZ(); // Leaf - optimize mob spawning + int i = 0; // Paper - throttle failed spawn attempts + BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn + if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn +- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); ++ BlockPos.MutableBlockPos mutableBlockPos = pos instanceof BlockPos.MutableBlockPos pos2 ? pos2 : new BlockPos.MutableBlockPos(); // Leaf - optimize mob spawning + //int i = 0; // Paper - throttle failed spawn attempts - move up + ++ // Leaf start - optimize mob spawning ++ long rand = level.random.nextLong(); ++ int bits = 0; + for (int i1 = 0; i1 < 3; i1++) { +- int x = pos.getX(); +- int z = pos.getZ(); +- int i2 = 6; ++ int x = posX; ++ int z = posZ; + MobSpawnSettings.SpawnerData spawnerData = null; + SpawnGroupData spawnGroupData = null; +- int ceil = Mth.ceil(level.random.nextFloat() * 4.0F); ++ int ceil = (int) ((rand & 0x3L) + 1L); ++ bits += 2; + int i3 = 0; + + for (int i4 = 0; i4 < ceil; i4++) { +- x += level.random.nextInt(6) - level.random.nextInt(6); +- z += level.random.nextInt(6) - level.random.nextInt(6); ++ int rand1=0,rand2=0,rand3=0,rand4=0,valuesNeeded=4; ++ while (valuesNeeded > 0) { ++ if (bits > 61) { ++ rand = level.random.nextLong(); ++ bits = 0; ++ } ++ int threeBits = (int) ((rand >>> bits) & 0x7L); ++ bits += 3; ++ if (threeBits != 7 && threeBits != 6) { ++ switch (valuesNeeded) { ++ case 1 -> rand4 = threeBits; ++ case 2 -> rand3 = threeBits; ++ case 3 -> rand2 = threeBits; ++ case 4 -> rand1 = threeBits; ++ } ++ valuesNeeded--; ++ } ++ } ++ x += rand1 - rand2; ++ z += rand3 - rand4; ++ // Leaf end - optimize mob spawning + mutableBlockPos.set(x, y, z); + double d = x + 0.5; + double d1 = z + 0.5; + Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, level.purpurConfig.mobSpawningIgnoreCreativePlayers); // Purpur - mob spawning option to ignore creative players + if (nearestPlayer != null) { + double d2 = nearestPlayer.distanceToSqr(d, y, d1); +- if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn ++ if (level.getWorldBorder().isWithinBounds(mutableBlockPos) && (chunk.getPos().longKey == ChunkPos.asLong(mutableBlockPos) || level.getChunkIfLoadedImmediately(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4) != null) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn // Leaf - optimize mob spawning + if (spawnerData == null) { + Optional randomSpawnMobAt = getRandomSpawnMobAt( + level, structureManager, generator, category, level.random, mutableBlockPos +@@ -368,8 +400,8 @@ public final class NaturalSpawner { + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { + return !(distance <= 576.0) +- && !level.getSharedSpawnPos().closerToCenterThan(new Vec3(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5), 24.0) +- && (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos)); ++ && !(level.getSharedSpawnPos().distToCenterSqr(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5) < 576.0) // Leaf - optimize mob spawning ++ && (ChunkPos.asLong(pos) == chunk.getPos().longKey || level.isNaturalSpawningAllowed(pos)); // Leaf - optimize mob spawning + } + + // Paper start - PreCreatureSpawnEvent +@@ -474,6 +506,17 @@ public final class NaturalSpawner { + } + } + ++ // Leaf start - optimize mob spawning ++ private static void mutableRandomPosWithin(BlockPos.MutableBlockPos pos1, Level level, LevelChunk chunk) { ++ ChunkPos pos = chunk.getPos(); ++ int randomX = pos.getMinBlockX() + level.random.nextInt(16); ++ int randomZ = pos.getMinBlockZ() + level.random.nextInt(16); ++ int surfaceY = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, randomX, randomZ) + 1; ++ int randomY = Mth.randomBetweenInclusive(level.random, level.getMinY(), surfaceY); ++ pos1.set(randomX, randomY, randomZ); ++ } ++ // Leaf end - optimize mob spawning ++ + private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) { + ChunkPos pos = chunk.getPos(); + int i = pos.getMinBlockX() + level.random.nextInt(16); diff --git a/leaf-server/minecraft-patches/features/0096-Pufferfish-Optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0096-Pufferfish-Optimize-mob-spawning.patch index 04738eaf..cc8eca55 100644 --- a/leaf-server/minecraft-patches/features/0096-Pufferfish-Optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0096-Pufferfish-Optimize-mob-spawning.patch @@ -22,7 +22,7 @@ and, in my opinion, worth the low risk of minor mob-spawning-related inconsistencies. diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 99e9fe7562520e37f8b0fa938b53a973b23c65c3..ff1f5943cb99353df6069060c67da86516c9c956 100644 +index c1b3d2b4c56b657ff51c8b27b4b80e0c0d01ddbe..18a0bf81562f61fd6ede72d51d836ae28e9226c3 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -285,6 +285,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { AtomicReference atomicReference = new AtomicReference<>(); +@@ -1046,6 +1047,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent serverLevel.updateLagCompensationTick(); // Paper - lag compensation net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers diff --git a/leaf-server/minecraft-patches/features/0100-Fix-Pufferfish-and-Purpur-patches.patch b/leaf-server/minecraft-patches/features/0100-Fix-Pufferfish-and-Purpur-patches.patch index 531947db..2d2474e1 100644 --- a/leaf-server/minecraft-patches/features/0100-Fix-Pufferfish-and-Purpur-patches.patch +++ b/leaf-server/minecraft-patches/features/0100-Fix-Pufferfish-and-Purpur-patches.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix Pufferfish and Purpur patches diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 5c0e7804555f9b789a96a98008dd1edf0be68104..62b82b753c36b5400e371952e0685115453bd953 100644 +index 271233087ad8a0ef8e90e1d518907e166f8235a2..ecf1e794c78962440da02aadfbe78d9782df2cf0 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -276,7 +276,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 0 && i <= maxSize) { diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 3190ed2171b0de32aeb8761aa3e510c968c45448..58c52eabba725be9fa5fde06be5cf69d0281ce5b 100644 +index cf958bb871137dc88a8c36be867f2192acba2687..2c50bde56da7174dcea828feedecb92c98347a45 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1735,6 +1735,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= resultLimit) { return players; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 58c52eabba725be9fa5fde06be5cf69d0281ce5b..83cbf865d83edcb4caea59a1a2b4932a915414be 100644 +index 2c50bde56da7174dcea828feedecb92c98347a45..ee07832a92b3f73bd85d13388276cf224743c0ed 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1638,7 +1638,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { AtomicReference atomicReference = new AtomicReference<>(); -@@ -1662,6 +1663,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) { if (entity.isRemoved()) { -@@ -1673,6 +1690,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop playerProfileResultCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() + .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) + .build(); ++ private static final com.github.benmanes.caffeine.cache.Cache playerSession = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() ++ .expireAfterWrite(org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResultTimeout, java.util.concurrent.TimeUnit.MINUTES) ++ .build(); + // Leaf end - Cache player profileResult public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { this.server = server; -@@ -303,9 +308,25 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -303,9 +311,30 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); try { @@ -29,15 +32,20 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..405b62c082017024abae7ccc1db5f74c - .hasJoinedServer(string1, string, this.getAddress()); + // Leaf start - Cache player profileResult + ProfileResult profileResult; -+ if (false) { // TODO ++ if (org.dreeam.leaf.config.modules.misc.Cache.cachePlayerProfileResult) { + profileResult = playerProfileResultCache.getIfPresent(string1); + -+ if (profileResult == null) { ++ InetAddress address = this.getAddress(); ++ InetAddress lastAddress = playerSession.getIfPresent(string1); ++ if (isInvalidSession(address, lastAddress)) { ++ // Send request to mojang server to verify session ++ // Result will be null if is invalid and will do disconnect logic below + profileResult = ServerLoginPacketListenerImpl.this.server + .getSessionService() -+ .hasJoinedServer(string1, string, this.getAddress()); -+ if (profileResult != null) { ++ .hasJoinedServer(string1, string, address); ++ if (profileResult != null && address != null) { + playerProfileResultCache.put(string1, profileResult); ++ playerSession.put(string1, address); + } + } + } else { @@ -49,3 +57,24 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..405b62c082017024abae7ccc1db5f74c if (profileResult != null) { GameProfile gameProfile = profileResult.profile(); // CraftBukkit start - fire PlayerPreLoginEvent +@@ -350,6 +379,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + // Paper end - Cache authenticator threads + } + ++ // Leaf start - Cache player profileResult ++ private static boolean isInvalidSession(@org.jetbrains.annotations.Nullable InetAddress currAddress, @org.jetbrains.annotations.Nullable InetAddress lastAddress) { ++ // Invalid address or non-public IP address ++ if (currAddress == null || ++ currAddress.isAnyLocalAddress() || ++ currAddress.isLinkLocalAddress() || ++ currAddress.isLoopbackAddress() || ++ currAddress.isSiteLocalAddress()) { ++ return true; ++ } ++ return !currAddress.equals(lastAddress); ++ } ++ // Leaf end - Cache player profileResult ++ + // CraftBukkit start + private GameProfile callPlayerPreLoginEvents(GameProfile gameprofile) throws Exception { // Paper - Add more fields to AsyncPlayerPreLoginEvent + // Paper start - Add Velocity IP Forwarding Support diff --git a/leaf-server/minecraft-patches/features/0162-Replace-brain-maps-with-optimized-collection.patch b/leaf-server/minecraft-patches/features/0162-Replace-brain-maps-with-optimized-collection.patch index fa9cc8ba..ffd21780 100644 --- a/leaf-server/minecraft-patches/features/0162-Replace-brain-maps-with-optimized-collection.patch +++ b/leaf-server/minecraft-patches/features/0162-Replace-brain-maps-with-optimized-collection.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Replace brain maps with optimized collection diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..b946faf899561b9e4f8399e16600eede54bb55a5 100644 +index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..4a0faaedacd7b925c5adad0280870e674cf7ab46 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java @@ -45,14 +45,18 @@ public class Brain { @@ -14,11 +14,12 @@ index 29fdf94db0308031edfe7915fc587a2aa5a1a18a..b946faf899561b9e4f8399e16600eede private static final int SCHEDULE_UPDATE_DELAY = 20; - private final Map, Optional>> memories = Maps.newHashMap(); - private final Map>, Sensor> sensors = Maps.newLinkedHashMap(); +- private final Map>>> availableBehaviorsByPriority = Maps.newTreeMap(); + // Leaf start - Replace brain maps with optimized collection -+ private final Map, Optional>> memories = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); -+ private final Map>, Sensor> sensors = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); ++ private final Map, Optional>> memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(); ++ private final Map>, Sensor> sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap<>(); ++ private final Map>>> availableBehaviorsByPriority = new it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap<>(); + // Leaf end - Replace brain maps with optimized collection - private final Map>>> availableBehaviorsByPriority = Maps.newTreeMap(); private Schedule schedule = Schedule.EMPTY; - private final Map, MemoryStatus>>> activityRequirements = Maps.newHashMap(); - private final Map>> activityMemoriesToEraseWhenStopped = Maps.newHashMap(); diff --git a/leaf-server/minecraft-patches/features/0175-Multithreaded-Tracker.patch b/leaf-server/minecraft-patches/features/0175-Multithreaded-Tracker.patch index c4cc557e..fe8e2542 100644 --- a/leaf-server/minecraft-patches/features/0175-Multithreaded-Tracker.patch +++ b/leaf-server/minecraft-patches/features/0175-Multithreaded-Tracker.patch @@ -71,7 +71,7 @@ index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e30 .add( new ClientboundUpdateAttributesPacket.AttributeSnapshot( diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe29a2d6f8b 100644 +index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..38a43e143906f2727025696a2895eafa8a77deb6 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -255,6 +255,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -104,26 +104,51 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 // Paper start - optimise entity tracker if (true) { this.newTrackerTick(); -@@ -1135,7 +1151,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1135,7 +1151,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final Entity entity; private final int range; SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + // Leaf start - Multithreaded tracker + public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0]; -+ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl ++ private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet nonSyncSeenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>() { ++ @Override ++ public boolean add(ServerPlayerConnection serverPlayerConnection) { ++ seenByUpdated = true; ++ return super.add(serverPlayerConnection); ++ } ++ ++ @Override ++ public boolean remove(Object k) { ++ seenByUpdated = true; ++ return super.remove(k); ++ } ++ ++ @Override ++ public void clear() { ++ seenByUpdated = true; ++ super.clear(); ++ } ++ }; ++ public final Set seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(nonSyncSeenBy) : nonSyncSeenBy; // Paper - Perf: optimise map impl ++ private volatile boolean seenByUpdated = true; + private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY; + public ServerPlayerConnection[] seenBy() { -+ return seenByArray; ++ if (!seenByUpdated) { ++ return seenByArray; ++ } else { ++ return seenBy.toArray(EMPTY_OBJECT_ARRAY); ++ } + } + public void seenByUpdated() { + this.seenByArray = this.seenBy.toArray(EMPTY_OBJECT_ARRAY); ++ seenByUpdated = false; + } + // Leaf end - Multithreaded tracker // Paper start - optimise entity tracker private long lastChunkUpdate = -1L; -@@ -1162,27 +1188,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1162,27 +1213,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.lastTrackedChunk = chunk; final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); @@ -145,13 +170,13 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 if (!players.contains(player)) { - this.removePlayer(player); + removed |= this.removePlayerMulti(player); - } - } ++ } ++ } + if (removed) { + this.seenByUpdated(); + } - } - } ++ } ++ } + // Leaf end - Multithreaded tracker + + // Leaf start - Multithreaded tracker @@ -199,8 +224,8 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 + } + if (removed) { + this.seenByUpdated(); -+ } -+ } + } + } + }; + + // Only update asynchronously for real player, and sync update for fake players @@ -212,8 +237,8 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 + } else { + updatePlayerTasks.run(); + return null; -+ } -+ } + } + } + // Leaf end - Multithreaded tracker @Override @@ -224,7 +249,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) { foundToRemove = true; break; -@@ -1193,12 +1287,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1193,12 +1312,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider return; } @@ -240,7 +265,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 } @Override -@@ -1208,10 +1303,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1208,10 +1328,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider if (this.seenBy.isEmpty()) { return; } @@ -254,7 +279,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 } @Override -@@ -1238,7 +1334,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1238,7 +1359,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcast(Packet packet) { @@ -263,7 +288,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 serverPlayerConnection.send(packet); } } -@@ -1259,21 +1355,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1259,21 +1380,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider } public void broadcastRemoved() { @@ -301,7 +326,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 // Paper start - remove allocation of Vec3D here // Vec3 vec3 = player.position().subtract(this.entity.position()); double vec3_dx = player.getX() - this.entity.getX(); -@@ -1301,6 +1410,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1301,6 +1435,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // CraftBukkit end if (flag) { if (this.seenBy.add(player.connection)) { @@ -309,7 +334,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe2 // Paper start - entity tracking events if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) { this.serverEntity.addPairing(player); -@@ -1309,6 +1419,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1309,6 +1444,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker } } else if (this.seenBy.remove(player.connection)) { diff --git a/leaf-server/minecraft-patches/features/0176-Nitori-Async-playerdata-saving.patch b/leaf-server/minecraft-patches/features/0176-Nitori-Async-playerdata-saving.patch index 4a47a035..9ea6c372 100644 --- a/leaf-server/minecraft-patches/features/0176-Nitori-Async-playerdata-saving.patch +++ b/leaf-server/minecraft-patches/features/0176-Nitori-Async-playerdata-saving.patch @@ -6,23 +6,6 @@ Subject: [PATCH] Nitori: Async playerdata saving Original license: GPL v3 Original project: https://github.com/Gensokyo-Reimagined/Nitori -diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 3ca7405318a4076000250203be4b91dbde95191b..2aed6f594c73bea5e8919e65cf04bc7eb6775c3e 100644 ---- a/net/minecraft/server/MinecraftServer.java -+++ b/net/minecraft/server/MinecraftServer.java -@@ -1067,6 +1067,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { diff --git a/leaf-server/minecraft-patches/features/0207-Remove-stream-in-villagers.patch b/leaf-server/minecraft-patches/features/0205-Remove-stream-in-villagers.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0207-Remove-stream-in-villagers.patch rename to leaf-server/minecraft-patches/features/0205-Remove-stream-in-villagers.patch diff --git a/leaf-server/minecraft-patches/features/0208-Optimize-baby-villager-sensor.patch b/leaf-server/minecraft-patches/features/0206-Optimize-baby-villager-sensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0208-Optimize-baby-villager-sensor.patch rename to leaf-server/minecraft-patches/features/0206-Optimize-baby-villager-sensor.patch diff --git a/leaf-server/minecraft-patches/features/0209-Only-player-pushable.patch b/leaf-server/minecraft-patches/features/0207-Only-player-pushable.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0209-Only-player-pushable.patch rename to leaf-server/minecraft-patches/features/0207-Only-player-pushable.patch diff --git a/leaf-server/minecraft-patches/features/0210-Remove-iterators-from-Inventory.patch b/leaf-server/minecraft-patches/features/0208-Remove-iterators-from-Inventory.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0210-Remove-iterators-from-Inventory.patch rename to leaf-server/minecraft-patches/features/0208-Remove-iterators-from-Inventory.patch diff --git a/leaf-server/minecraft-patches/features/0212-Cache-eligible-players-for-despawn-checks.patch b/leaf-server/minecraft-patches/features/0209-Cache-eligible-players-for-despawn-checks.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0212-Cache-eligible-players-for-despawn-checks.patch rename to leaf-server/minecraft-patches/features/0209-Cache-eligible-players-for-despawn-checks.patch index d58b850b..2285bceb 100644 --- a/leaf-server/minecraft-patches/features/0212-Cache-eligible-players-for-despawn-checks.patch +++ b/leaf-server/minecraft-patches/features/0209-Cache-eligible-players-for-despawn-checks.patch @@ -38,10 +38,10 @@ index ccd6f16e244745ee0702504dbea710485037a3e3..2ecb73fc7b6754ade93bf16b48c623e6 .forEach( entity -> { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index e97a93c97e1822f969dce2d30dd915db5d3d14cf..ccd442b24693bc9269cc8ab3e44887d0ad3eadbd 100644 +index 18125ed336c3425f123232b405a8af9ee3a2ba7d..d507544efafe74ecaffd6a063eff152d349ec76a 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -1519,6 +1519,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1518,6 +1518,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc this.containerMenu.broadcastChanges(); } diff --git a/leaf-server/minecraft-patches/features/0213-Slightly-optimise-getNearestPlayer.patch b/leaf-server/minecraft-patches/features/0210-Slightly-optimise-getNearestPlayer.patch similarity index 83% rename from leaf-server/minecraft-patches/features/0213-Slightly-optimise-getNearestPlayer.patch rename to leaf-server/minecraft-patches/features/0210-Slightly-optimise-getNearestPlayer.patch index ac9e99a1..4733c36c 100644 --- a/leaf-server/minecraft-patches/features/0213-Slightly-optimise-getNearestPlayer.patch +++ b/leaf-server/minecraft-patches/features/0210-Slightly-optimise-getNearestPlayer.patch @@ -20,10 +20,10 @@ One-time operation: Convert distance to squared distance Total operations: ~3 × n (in the no-predicate case) or ~4 × n (with predicate) diff --git a/net/minecraft/world/level/EntityGetter.java b/net/minecraft/world/level/EntityGetter.java -index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42cf0554d9 100644 +index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..308e00947da3fe4888fb1b1b9fa11ac5da46663b 100644 --- a/net/minecraft/world/level/EntityGetter.java +++ b/net/minecraft/world/level/EntityGetter.java -@@ -201,23 +201,42 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst +@@ -201,23 +201,43 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst } // Paper end - Affects Spawning API @@ -41,9 +41,10 @@ index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42 - for (Player player1 : this.players()) { - if (predicate == null || predicate.test(player1)) { ++ List players = this.players(); + if (predicate == null) { -+ for (int i = 0; i < this.players().size(); i++) { -+ Player player1 = this.players().get(i); ++ for (int i = 0, playersSize = players.size(); i < playersSize; i++) { ++ Player player1 = players.get(i); double d1 = player1.distanceToSqr(x, y, z); - if ((distance < 0.0 || d1 < distance * distance) && (d == -1.0 || d1 < d)) { - d = d1; @@ -53,8 +54,8 @@ index 670860df81a3abfc1b8b53be505fce0ee32ee2c4..083a2b5da246113913bcd5d0b2b9be42 } } + } else { -+ for (int i = 0; i < this.players().size(); i++) { -+ Player player1 = this.players().get(i); ++ for (int i = 0, playersSize = players.size(); i < playersSize; i++) { ++ Player player1 = players.get(i); + if (predicate.test(player1)) { + double d1 = player1.distanceToSqr(x, y, z); + if (d1 < distance) { diff --git a/leaf-server/minecraft-patches/features/0211-Alternative-Brain-Behaviour.patch b/leaf-server/minecraft-patches/features/0211-Alternative-Brain-Behaviour.patch deleted file mode 100644 index 1932ca40..00000000 --- a/leaf-server/minecraft-patches/features/0211-Alternative-Brain-Behaviour.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Taiyou06 -Date: Fri, 14 Feb 2025 14:58:59 +0100 -Subject: [PATCH] Alternative Brain Behaviour - -In the test, this can give ~54.87% improvement (~25712ms -> ~11604ms), -under 1024 villagers situation. - -diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index e78cef4c0ee6cff7ec2f27ced12c96be67dc7a0e..e3d0186e681168840836690acd402b774bc464b5 100644 ---- a/net/minecraft/world/entity/ai/Brain.java -+++ b/net/minecraft/world/entity/ai/Brain.java -@@ -268,23 +268,52 @@ public class Brain { - return this.activeActivities; - } - -+ // Leaf start - Alternative Brain Behaviour -+ private ObjectArrayList> runningBehaviorsCache; -+ private long lastRunningBehaviorCheck = -1; -+ // Leaf end - Alternative Brain Behaviour -+ - @Deprecated - @VisibleForDebug - public List> getRunningBehaviors() { -- List> list = new ObjectArrayList<>(); -+ // Leaf start - Alternative Brain Behaviour -+ long currentTick = getCurrentTick(); -+ -+ // Use cached result if within update interval -+ if (runningBehaviorsCache != null && (currentTick - lastRunningBehaviorCheck) < org.dreeam.leaf.config.modules.opt.BrainRunningBehaviorCacheUpdate.interval) { -+ return runningBehaviorsCache; -+ } -+ -+ // Initialize or reuse cache list -+ if (runningBehaviorsCache == null) { -+ runningBehaviorsCache = new ObjectArrayList<>(32); -+ } else { -+ runningBehaviorsCache.clear(); -+ } -+ -+ for (Map>> activityMap : availableBehaviorsByPriority.values()) { -+ for (Set> behaviors : activityMap.values()) { -+ if (behaviors.isEmpty()) continue; - -- for (Map>> map : this.availableBehaviorsByPriority.values()) { -- for (Set> set : map.values()) { -- for (BehaviorControl behaviorControl : set) { -- if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { -- list.add(behaviorControl); -+ for (BehaviorControl behavior : behaviors) { -+ if (behavior.getStatus() == Behavior.Status.RUNNING) { -+ runningBehaviorsCache.add(behavior); - } - } - } - } - -- return list; -+ lastRunningBehaviorCheck = currentTick; -+ -+ return runningBehaviorsCache; -+ } -+ -+ // Helper method to get current tick -+ private long getCurrentTick() { -+ // This should be implemented to return the current game tick -+ return System.nanoTime() / 50_000_000; // Approximate tick time of 50ms - } -+ // Leaf end - Alternative Brain Behaviour - - public void useDefaultActivity() { - this.setActiveActivity(this.defaultActivity); diff --git a/leaf-server/minecraft-patches/features/0214-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch b/leaf-server/minecraft-patches/features/0211-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch similarity index 91% rename from leaf-server/minecraft-patches/features/0214-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch rename to leaf-server/minecraft-patches/features/0211-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch index 33c1c7e3..212977eb 100644 --- a/leaf-server/minecraft-patches/features/0214-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch +++ b/leaf-server/minecraft-patches/features/0211-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use ensureCapacity to pre-populate the size of ticking chunks diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 14b2abc51fa5d2caa56adfaf50784296c5668ae6..0813505f96a1a6bb2cbfb05a97c90241f0c5b1c8 100644 +index 083b49c8fbdbde96b5be3e5531c21b2c96371b60..d77197ff56bd7acd7747af7d0dc937c31fa86a17 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -733,6 +733,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider diff --git a/leaf-server/minecraft-patches/features/0215-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch b/leaf-server/minecraft-patches/features/0212-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch similarity index 92% rename from leaf-server/minecraft-patches/features/0215-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch rename to leaf-server/minecraft-patches/features/0212-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch index 75019207..e424b8cd 100644 --- a/leaf-server/minecraft-patches/features/0215-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch +++ b/leaf-server/minecraft-patches/features/0212-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch @@ -7,7 +7,7 @@ Subject: [PATCH] Directly use the pre-filtered ticking chunks list as the This patch uses already pre filtered chunks, which completely skips the isChunkNearPlayer check diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 0813505f96a1a6bb2cbfb05a97c90241f0c5b1c8..051d839fece06cac8ad79d8d936237a66cd01ae4 100644 +index d77197ff56bd7acd7747af7d0dc937c31fa86a17..b0a5ded4012e892631f20829166e86fac8491339 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -739,13 +739,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider diff --git a/leaf-server/minecraft-patches/features/0216-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0213-Bulk-writes-to-writeLongArray-during-chunk-loading.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0216-Bulk-writes-to-writeLongArray-during-chunk-loading.patch rename to leaf-server/minecraft-patches/features/0213-Bulk-writes-to-writeLongArray-during-chunk-loading.patch diff --git a/leaf-server/minecraft-patches/features/0217-Improve-sorting-in-SortedArraySet.patch b/leaf-server/minecraft-patches/features/0214-Improve-sorting-in-SortedArraySet.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0217-Improve-sorting-in-SortedArraySet.patch rename to leaf-server/minecraft-patches/features/0214-Improve-sorting-in-SortedArraySet.patch diff --git a/leaf-server/minecraft-patches/features/0218-Make-removeIf-slightly-faster.patch b/leaf-server/minecraft-patches/features/0215-Make-removeIf-slightly-faster.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0218-Make-removeIf-slightly-faster.patch rename to leaf-server/minecraft-patches/features/0215-Make-removeIf-slightly-faster.patch diff --git a/leaf-server/minecraft-patches/features/0219-Optimize-LinearPalette.patch b/leaf-server/minecraft-patches/features/0216-Optimize-LinearPalette.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0219-Optimize-LinearPalette.patch rename to leaf-server/minecraft-patches/features/0216-Optimize-LinearPalette.patch diff --git a/leaf-server/minecraft-patches/features/0220-Slightly-optimized-VarInt-write.patch b/leaf-server/minecraft-patches/features/0217-Slightly-optimized-VarInt-write.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0220-Slightly-optimized-VarInt-write.patch rename to leaf-server/minecraft-patches/features/0217-Slightly-optimized-VarInt-write.patch diff --git a/leaf-server/minecraft-patches/features/0221-Rewrite-ClientboundLightUpdatePacketData.patch b/leaf-server/minecraft-patches/features/0218-Rewrite-ClientboundLightUpdatePacketData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0221-Rewrite-ClientboundLightUpdatePacketData.patch rename to leaf-server/minecraft-patches/features/0218-Rewrite-ClientboundLightUpdatePacketData.patch diff --git a/leaf-server/minecraft-patches/features/0222-Async-chunk-send.patch b/leaf-server/minecraft-patches/features/0219-Async-chunk-send.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0222-Async-chunk-send.patch rename to leaf-server/minecraft-patches/features/0219-Async-chunk-send.patch diff --git a/leaf-server/minecraft-patches/features/0223-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0220-Spawner-Configurations.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0223-Spawner-Configurations.patch rename to leaf-server/minecraft-patches/features/0220-Spawner-Configurations.patch diff --git a/leaf-server/minecraft-patches/features/0224-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0221-SparklyPaper-Parallel-world-ticking.patch similarity index 93% rename from leaf-server/minecraft-patches/features/0224-SparklyPaper-Parallel-world-ticking.patch rename to leaf-server/minecraft-patches/features/0221-SparklyPaper-Parallel-world-ticking.patch index 4096a1dc..e35dfbe0 100644 --- a/leaf-server/minecraft-patches/features/0224-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0221-SparklyPaper-Parallel-world-ticking.patch @@ -41,6 +41,29 @@ index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..8d53cb917e9f623a67aba066c6a21f27 boolean ret = false; final boolean canProcessFullUpdates = processFullUpdates & isTickThread; +diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java +index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..b288d57d9f7bd0ccf1877cf9920bd67288ff22f7 100644 +--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java ++++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java +@@ -829,14 +829,10 @@ public final class RedstoneWireTurbo { + j = getMaxCurrentStrength(upd, j); + int l = 0; + +- wire.shouldSignal = false; +- // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated, +- // and I'm not ready to try to replicate even more functionality from +- // elsewhere in Minecraft into this accelerator. So sadly, we must +- // suffer the performance hit of this very expensive call. If there +- // is consistency to what this call returns, we may be able to cache it. +- final int k = worldIn.getBestNeighborSignal(upd.self); +- wire.shouldSignal = true; ++ // Leaf start - SparklyPaper - parallel world ticking ++ // This now correctly calls the (conditionally) thread-safe method in RedStoneWireBlock ++ final int k = wire.getBlockSignal(worldIn, upd.self); ++ // Leaf end - SparklyPaper - parallel world ticking + + // The variable 'k' holds the maximum redstone power value of any adjacent blocks. + // If 'k' has the highest level of all neighbors, then the power level of this diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java index 582e012222123e5001c34153f2ee1ab1d08935fd..c0bce2293d07ca58cc5bc9e036ab8dcac06c1566 100644 --- a/net/minecraft/core/dispenser/DispenseItemBehavior.java @@ -57,7 +80,7 @@ index 582e012222123e5001c34153f2ee1ab1d08935fd..c0bce2293d07ca58cc5bc9e036ab8dca List states = new java.util.ArrayList<>(level.capturedBlockStates.values()); level.capturedBlockStates.clear(); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index bbd2b327c658b56a1fcf30c8b77cab987d688fcc..c9b8bc9592d5fce77750de89fa886db383d92a89 100644 +index 4bf840705bec251aa7e5595fa16bf8e1a9a7d2e9..543bdb926426bfd5be01d0c23e8c9e274b94485c 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -290,6 +290,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop serverPlayer1.connection.suspendFlushing()); this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit -@@ -1731,28 +1762,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> oldLevels = this.levels; Map, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); newLevels.remove(level.dimension()); @@ -373,7 +396,7 @@ index 54910c2e1d6e6bb556e536fda060bd09402e04e8..72e871b8c7fee9b5cbd567e03baee80e // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 0f9d18dd29e210ad656da211a3cb1cb25cd4efb1..b3acfc7e00797ea16cc2c1793452f3ed97b9c61a 100644 +index 6d5a15122079f2d1568ceb7086db21ad454f58e6..9dd6205e1cdd2124ab9d91f0a1e344eb6aa1fb2e 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -175,7 +175,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -617,7 +640,7 @@ index 2ecb73fc7b6754ade93bf16b48c623e6b3a955a9..85bfc91ff163a2a564b7b610e27ff90e // Paper start - extra debug info if (entity.valid) { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index ccd442b24693bc9269cc8ab3e44887d0ad3eadbd..ac7ac519ecc58a31c4b9efc24f054ec014851fb4 100644 +index d507544efafe74ecaffd6a063eff152d349ec76a..bc955da0dff79262dace84d255f27b3271a91ca5 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -456,6 +456,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -645,7 +668,7 @@ index ccd442b24693bc9269cc8ab3e44887d0ad3eadbd..ac7ac519ecc58a31c4b9efc24f054ec0 // CraftBukkit start /* this.isChangingDimension = true; -@@ -1748,6 +1752,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1747,6 +1751,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return OptionalInt.empty(); } else { // CraftBukkit start @@ -658,7 +681,7 @@ index ccd442b24693bc9269cc8ab3e44887d0ad3eadbd..ac7ac519ecc58a31c4b9efc24f054ec0 this.containerMenu = abstractContainerMenu; // Moved up if (!this.isImmobile()) this.connection -@@ -1812,6 +1822,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -1811,6 +1821,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } @Override public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { @@ -671,7 +694,7 @@ index ccd442b24693bc9269cc8ab3e44887d0ad3eadbd..ac7ac519ecc58a31c4b9efc24f054ec0 // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 8b1652805a2ac9ebae9c99dd77e81c7a484a7abb..e4bccc642abe15b0ec4b1cf2634801065a27e11a 100644 +index 34c5c2e3d6b0706cb91ec0171969cf81e94741a4..a1249d66c3693374b0f57c83302d05a7cbc2a306 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -252,6 +252,8 @@ public abstract class PlayerList { @@ -688,12 +711,12 @@ index 8b1652805a2ac9ebae9c99dd77e81c7a484a7abb..e4bccc642abe15b0ec4b1cf263480106 public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, @Nullable org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, @Nullable org.bukkit.Location location) { + // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { -+ if (location != null) // Leaf - THIS CAN BE NULL; see PlayerList::respawn(ServerPlayer, boolean, Entity.RemovalReason, PlayerRespawnEvent.RespawnReason) -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + player.serverLevel().getWorld().getName() + " to world " + location.getWorld().getName()); -+ else -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, respawning in world " + player.serverLevel().getWorld().getName()); -+ // SparklyPaper end - parallel world ticking (additional concurrency issues logs) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !this.server.isSameThread()) { ++ // Respawning is a complex operation that modifies global player lists and can interact with multiple ++ // worlds. It must be executed on the main server thread to ensure thread safety. We block the ++ // calling (world) thread to wait for the result, preserving the synchronous API contract of this method. ++ org.bukkit.Location finalLocation = location; ++ return this.server.submit(() -> this.respawn(player, keepInventory, reason, eventReason, finalLocation)).join(); + } + // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) player.stopRiding(); // CraftBukkit @@ -1104,10 +1127,21 @@ index d306f5f524dc64618df94c9783c2168dc561a5e3..2e0aec5327d92f89f38bbc393b3ba705 return true; } else { diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..3902c21a0a4f456a4e1b966a0301c15f8c20a298 100644 +index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..ccc98d71da3c1150878f96801fbf65bfa1a64472 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -283,7 +283,12 @@ public class RedStoneWireBlock extends Block { +@@ -65,7 +65,10 @@ public class RedStoneWireBlock extends Block { + private final Function shapes; + private final BlockState crossState; + private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this); ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) + public boolean shouldSignal = true; ++ private final ThreadLocal shouldSignalTL = ThreadLocal.withInitial(() -> true); ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + + @Override + public MapCodec codec() { +@@ -283,7 +286,12 @@ public class RedStoneWireBlock extends Block { if (orientation != null) { source = pos.relative(orientation.getFront().getOpposite()); } @@ -1121,7 +1155,7 @@ index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..3902c21a0a4f456a4e1b966a0301c15f return; } updatePowerStrength(worldIn, pos, state, orientation, blockAdded); -@@ -311,7 +316,12 @@ public class RedStoneWireBlock extends Block { +@@ -311,7 +319,12 @@ public class RedStoneWireBlock extends Block { // [Space Walker] suppress shape updates and emit those manually to // bypass the new neighbor update stack. if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) { @@ -1135,6 +1169,76 @@ index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..3902c21a0a4f456a4e1b966a0301c15f } } } +@@ -328,10 +341,19 @@ public class RedStoneWireBlock extends Block { + } + + public int getBlockSignal(Level level, BlockPos pos) { +- this.shouldSignal = false; +- int bestNeighborSignal = level.getBestNeighborSignal(pos); +- this.shouldSignal = true; +- return bestNeighborSignal; ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { ++ this.shouldSignalTL.set(false); ++ int bestNeighborSignal = level.getBestNeighborSignal(pos); ++ this.shouldSignalTL.set(true); ++ return bestNeighborSignal; ++ } else { ++ this.shouldSignal = false; ++ int bestNeighborSignal = level.getBestNeighborSignal(pos); ++ this.shouldSignal = true; ++ return bestNeighborSignal; ++ } ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + private void checkCornerChangeAt(Level level, BlockPos pos) { +@@ -422,12 +444,21 @@ public class RedStoneWireBlock extends Block { + + @Override + protected int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { +- return !this.shouldSignal ? 0 : blockState.getSignal(blockAccess, pos, side); ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ boolean signal = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.shouldSignalTL.get() : this.shouldSignal; ++ return !signal ? 0 : blockState.getSignal(blockAccess, pos, side); ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + @Override + protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { +- if (this.shouldSignal && side != Direction.DOWN) { ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ boolean signal; ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ++ signal = this.shouldSignalTL.get(); ++ else ++ signal = this.shouldSignal; ++ if (signal && side != Direction.DOWN) { + int powerValue = blockState.getValue(POWER); + if (powerValue == 0) { + return 0; +@@ -441,6 +472,7 @@ public class RedStoneWireBlock extends Block { + return 0; + } + } ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + + protected static boolean shouldConnectTo(BlockState state) { + return shouldConnectTo(state, null); +@@ -459,7 +491,12 @@ public class RedStoneWireBlock extends Block { + + @Override + protected boolean isSignalSource(BlockState state) { +- return this.shouldSignal; ++ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable) ++ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ++ return this.shouldSignalTL.get(); ++ else ++ return this.shouldSignal; ++ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable) + } + + public static int getColorForPower(int power) { diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java index a22cb810622e0ae97bc2a0d6390d026d9482b783..3e7478e959da3a0191de6c76b80cbb9b625b6898 100644 --- a/net/minecraft/world/level/block/SaplingBlock.java diff --git a/leaf-server/minecraft-patches/features/0225-SparklyPaper-Track-each-world-MSPT.patch b/leaf-server/minecraft-patches/features/0222-SparklyPaper-Track-each-world-MSPT.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0225-SparklyPaper-Track-each-world-MSPT.patch rename to leaf-server/minecraft-patches/features/0222-SparklyPaper-Track-each-world-MSPT.patch index 276e4ba1..494958da 100644 --- a/leaf-server/minecraft-patches/features/0225-SparklyPaper-Track-each-world-MSPT.patch +++ b/leaf-server/minecraft-patches/features/0222-SparklyPaper-Track-each-world-MSPT.patch @@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Track each world MSPT Original project: https://github.com/SparklyPower/SparklyPaper diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index c9b8bc9592d5fce77750de89fa886db383d92a89..1dc599e28682b59c8389ff2ada100b405bf97240 100644 +index 543bdb926426bfd5be01d0c23e8c9e274b94485c..9933a60db91d68f76df33ccddea0852fd59b8983 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1687,7 +1687,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Sun, 6 Apr 2025 11:22:35 +0200 +Subject: [PATCH] More virtual threads + + +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index 9918572306e983281d05c6d28c8a5d843348ad2d..2491ca5bcabcf8ae99ef990eed4e1fd94f799991 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -98,7 +98,8 @@ public class Util { + public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool + private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); + // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread +- public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { ++ // Leaf start - More virtual threads ++ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { + + private final AtomicInteger count = new AtomicInteger(); + +@@ -111,7 +112,30 @@ public class Util { + }); + return ret; + } +- }); ++ }); */ ++ ++ private static ExecutorService createProfileExecutor() { ++ final java.util.concurrent.ThreadFactory factory; ++ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) { ++ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); ++ } else { ++ factory = new java.util.concurrent.ThreadFactory() { ++ private final AtomicInteger count = new AtomicInteger(); ++ ++ @Override ++ public Thread newThread(Runnable run) { ++ Thread ret = new Thread(run); ++ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement()); ++ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> { ++ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); ++ }); ++ return ret; ++ } ++ }; ++ } ++ return Executors.newFixedThreadPool(2, factory); ++ } ++ // Leaf end - More virtual threads + // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); + public static final int LINEAR_LOOKUP_THRESHOLD = 8; diff --git a/leaf-server/minecraft-patches/features/0243-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0240-Async-target-finding.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0243-Async-target-finding.patch rename to leaf-server/minecraft-patches/features/0240-Async-target-finding.patch index d4bf2872..9ef1594f 100644 --- a/leaf-server/minecraft-patches/features/0243-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0240-Async-target-finding.patch @@ -106,7 +106,7 @@ index 7b686d834e4eb36be5758b0e0a846a70d1e2294b..956d48fb7146b9eb2a5b5b4e23a83f60 final EntityCollectionBySection byType = this.entitiesByType.get(entity.getType()); byType.removeEntity(entity, sectionIndex); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 1dc599e28682b59c8389ff2ada100b405bf97240..c2c904565df5fb320b98307a4d451066286ad726 100644 +index 9933a60db91d68f76df33ccddea0852fd59b8983..14c4dfb497ab050adfffebb8d75e6fe89c38effc 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -291,6 +291,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { AtomicReference atomicReference = new AtomicReference<>(); -@@ -1092,6 +1093,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Sun, 6 Apr 2025 11:22:35 +0200 -Subject: [PATCH] More virtual threads - - -diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java -index 9918572306e983281d05c6d28c8a5d843348ad2d..1d4ad1370ca041753ce765b1a2feddae59f1f8ca 100644 ---- a/net/minecraft/Util.java -+++ b/net/minecraft/Util.java -@@ -98,7 +98,8 @@ public class Util { - public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool - private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); - // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread -- public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { -+ // Leaf start - More virtual threads -+ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { - - private final AtomicInteger count = new AtomicInteger(); - -@@ -111,7 +112,30 @@ public class Util { - }); - return ret; - } -- }); -+ }); */ -+ -+ private static ExecutorService createProfileExecutor() { -+ final java.util.concurrent.ThreadFactory factory; -+ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) { -+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); -+ } else { -+ factory = new java.util.concurrent.ThreadFactory() { -+ private final AtomicInteger count = new AtomicInteger(); -+ -+ @Override -+ public Thread newThread(Runnable run) { -+ Thread ret = new Thread(run); -+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement()); -+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> { -+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); -+ }); -+ return ret; -+ } -+ }; -+ } -+ return Executors.newFixedThreadPool(2, factory); -+ } -+ // Leaf end - More virtual threads - // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread - private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); - public static final int LINEAR_LOOKUP_THRESHOLD = 8; -@@ -255,16 +279,31 @@ public class Util { - } - - private static TracingExecutor makeIoExecutor(String name, boolean daemon) { -- AtomicInteger atomicInteger = new AtomicInteger(1); -- return new TracingExecutor(Executors.newCachedThreadPool(task -> { -- Thread thread = new Thread(task); -- String string = name + atomicInteger.getAndIncrement(); -- TracyClient.setThreadName(string, name.hashCode()); -- thread.setName(string); -- thread.setDaemon(daemon); -- thread.setUncaughtExceptionHandler(Util::onThreadException); -- return thread; -- })); -+ // Leaf start - More virtual threads -+ final java.util.concurrent.ThreadFactory factory; -+ final boolean useVirtualThreads; // Gale - virtual thread support -+ if (name.startsWith("Download-")) { // Gale - virtual thread support -+ useVirtualThreads = org.dreeam.leaf.config.modules.opt.VT4DownloadPool.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported(); // Gale - virtual thread support -+ } else { -+ useVirtualThreads = false; -+ } -+ -+ if (useVirtualThreads) { -+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory(); -+ } else { -+ AtomicInteger atomicInteger = new AtomicInteger(1); -+ factory = task -> { -+ Thread thread = new Thread(task); -+ String string = name + atomicInteger.getAndIncrement(); -+ TracyClient.setThreadName(string, name.hashCode()); -+ thread.setName(string); -+ thread.setDaemon(daemon); -+ thread.setUncaughtExceptionHandler(Util::onThreadException); -+ return thread; -+ }; -+ } -+ return new TracingExecutor(Executors.newCachedThreadPool(factory)); -+ // Leaf end - More virtual threads - } - - // Paper start - Separate dimension data IO pool -@@ -1109,7 +1148,7 @@ public class Util { - } - - public static Typed readTypedOrThrow(Type type, Dynamic data, boolean partial) { -- DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); -+ DataResult> dataResult = type.readTyped(data).map(Pair::getFirst); // Paper - Fix generics issue // Gale - Fix generics issue - - try { - return partial ? dataResult.getPartialOrThrow(IllegalStateException::new) : dataResult.getOrThrow(IllegalStateException::new); -@@ -1158,7 +1197,7 @@ public class Util { - } - - public void openUri(URI uri) { -- throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code -+ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code // Leaf - More virtual threads - This method is not useful on dedicated servers - } - - public void openFile(File file) { diff --git a/leaf-server/minecraft-patches/features/0245-Optimise-MobEffectUtil-getDigSpeedAmplification.patch b/leaf-server/minecraft-patches/features/0242-Optimise-MobEffectUtil-getDigSpeedAmplification.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0245-Optimise-MobEffectUtil-getDigSpeedAmplification.patch rename to leaf-server/minecraft-patches/features/0242-Optimise-MobEffectUtil-getDigSpeedAmplification.patch diff --git a/leaf-server/minecraft-patches/features/0246-Optimise-chunkUnloads.patch b/leaf-server/minecraft-patches/features/0243-Optimise-chunkUnloads.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0246-Optimise-chunkUnloads.patch rename to leaf-server/minecraft-patches/features/0243-Optimise-chunkUnloads.patch diff --git a/leaf-server/minecraft-patches/features/0247-Optimize-BlockEntityType-isValid.patch b/leaf-server/minecraft-patches/features/0244-Optimize-BlockEntityType-isValid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0247-Optimize-BlockEntityType-isValid.patch rename to leaf-server/minecraft-patches/features/0244-Optimize-BlockEntityType-isValid.patch diff --git a/leaf-server/minecraft-patches/features/0248-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch b/leaf-server/minecraft-patches/features/0245-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch similarity index 97% rename from leaf-server/minecraft-patches/features/0248-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch rename to leaf-server/minecraft-patches/features/0245-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch index 0b9032aa..6164374f 100644 --- a/leaf-server/minecraft-patches/features/0248-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch +++ b/leaf-server/minecraft-patches/features/0245-PaperPR-Add-ticket-on-player-join-to-avoid-chunk-loa.patch @@ -27,7 +27,7 @@ index 3a78e7512772fd3f7cf8f221e3a72474def14bea..ba52af914e9e231caa0ac50562e9a692 public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL; public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e4bccc642abe15b0ec4b1cf2634801065a27e11a..6a9aa085956ddd27e8910dd524ecb79caf9ae0ba 100644 +index a1249d66c3693374b0f57c83302d05a7cbc2a306..2ea112ed64d5dc7c5eb365e7a518cf0e8dd4927f 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -434,6 +434,13 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0249-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0246-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0249-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch rename to leaf-server/minecraft-patches/features/0246-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch diff --git a/leaf-server/minecraft-patches/features/0250-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0247-Sakura-copy-EntityList-implementation-to-BasicEntity.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0250-Sakura-copy-EntityList-implementation-to-BasicEntity.patch rename to leaf-server/minecraft-patches/features/0247-Sakura-copy-EntityList-implementation-to-BasicEntity.patch diff --git a/leaf-server/minecraft-patches/features/0251-Protocol-Core.patch b/leaf-server/minecraft-patches/features/0248-Protocol-Core.patch similarity index 78% rename from leaf-server/minecraft-patches/features/0251-Protocol-Core.patch rename to leaf-server/minecraft-patches/features/0248-Protocol-Core.patch index fb69fefd..70b04581 100644 --- a/leaf-server/minecraft-patches/features/0251-Protocol-Core.patch +++ b/leaf-server/minecraft-patches/features/0248-Protocol-Core.patch @@ -22,10 +22,10 @@ index 56fd1ed7ccaf96e7eedea60fbdbf7f934939d563..d2f522ea6d0a209496848af073c9af1c } diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index c2c904565df5fb320b98307a4d451066286ad726..84b90f496e5ab59d43503301fe935627af123e3f 100644 +index 14c4dfb497ab050adfffebb8d75e6fe89c38effc..54f5336b5bb56cb9dfda2b75fff07bae0b27cfd3 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1833,6 +1833,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop mountedOrDismounted(List entities) { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index ac7ac519ecc58a31c4b9efc24f054ec014851fb4..59a61ac969e79966484a839b7d9ca0ac50e80994 100644 +index bc955da0dff79262dace84d255f27b3271a91ca5..7abc297d91d6e1b21b855424b345dba4f4df37b6 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -805,6 +805,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -71,7 +59,7 @@ index 18f0d486c478087f404d8bb6cd840079e2c8d239..1a6f1c5f4cf13ee50bc8445845cbb973 final byte[] data = discardedPayload.data(); try { diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 6a9aa085956ddd27e8910dd524ecb79caf9ae0ba..e0f6b82d9836cb73f6fe631071bb1a0c5ec77eab 100644 +index 2ea112ed64d5dc7c5eb365e7a518cf0e8dd4927f..24a5fd508e36238aade92fcae1c97cb38159506a 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -686,6 +686,7 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0252-Reduce-PlayerChunk-Updates.patch b/leaf-server/minecraft-patches/features/0249-Reduce-PlayerChunk-Updates.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0252-Reduce-PlayerChunk-Updates.patch rename to leaf-server/minecraft-patches/features/0249-Reduce-PlayerChunk-Updates.patch diff --git a/leaf-server/minecraft-patches/features/0253-Async-switch-connection-state.patch b/leaf-server/minecraft-patches/features/0250-Async-switch-connection-state.patch similarity index 86% rename from leaf-server/minecraft-patches/features/0253-Async-switch-connection-state.patch rename to leaf-server/minecraft-patches/features/0250-Async-switch-connection-state.patch index 8fe1a4fa..50dce0b1 100644 --- a/leaf-server/minecraft-patches/features/0253-Async-switch-connection-state.patch +++ b/leaf-server/minecraft-patches/features/0250-Async-switch-connection-state.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Async switch connection state diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f569350a53 100644 +index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..3dce0665e7438d2994a86450e31fb2a10431df9b 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -342,6 +342,11 @@ public class Connection extends SimpleChannelInboundHandler> { @@ -36,22 +36,11 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f5 } } -@@ -369,7 +381,38 @@ public class Connection extends SimpleChannelInboundHandler> { - } +@@ -373,6 +385,31 @@ public class Connection extends SimpleChannelInboundHandler> { + } + } - boolean flag = protocolInfo.id() == ConnectionProtocol.LOGIN; -- syncAfterConfigurationChange(this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag))); -+ var cf = this.channel.writeAndFlush(outboundConfigurationTask.andThen(context -> this.sendLoginDisconnect = flag)); -+ // Leaf start - Async switch connection state -+ if (org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) { -+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { -+ throw new IllegalStateException("Thread failed netty thread check: Switching outbound protocol state use setupOutboundProtocolAsync instead"); -+ } -+ } -+ syncAfterConfigurationChange(cf); -+ } -+ } -+ ++ // Leaf start - Async switch connection state + public @Nullable ChannelFuture setupOutboundProtocolAsync(ProtocolInfo protocolInfo) { + if (protocolInfo.flow() != this.getSending()) { + throw new IllegalStateException("Invalid outbound protocol: " + protocolInfo.id()); @@ -72,10 +61,13 @@ index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..c83ee2137a57e62003b1d20c3ceea9f5 + return cf; + } + return null; -+ // Leaf end - Async switch connection state - } - } - ++ } ++ } ++ // Leaf end - Async switch connection state ++ + public void setListenerForServerboundHandshake(PacketListener packetListener) { + if (this.packetListener != null) { + throw new IllegalStateException("Listener already set"); diff --git a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe09ea7015a 100644 --- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java @@ -118,10 +110,10 @@ index 2e9eb04c7c4342393c05339906c267bca9ff29b1..53b9daa909c2b89046d5af515e17afe0 try { PlayerList playerList = this.server.getPlayerList(); diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 405b62c082017024abae7ccc1db5f74caab1eabf..1ad1fc46775a473e6f7fd97eac4b8c7110c7332b 100644 +index 6b0b0ccc18a5da05685867656705e1fcf94b5891..889bab9613fb32d239bb0613db4a900a442116a3 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -471,11 +471,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, +@@ -493,11 +493,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } @@ -150,7 +142,7 @@ index 405b62c082017024abae7ccc1db5f74caab1eabf..1ad1fc46775a473e6f7fd97eac4b8c71 net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); - this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); -+ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); // Leaf - Async switch connection state ++ if (!org.dreeam.leaf.config.modules.network.AlternativeJoin.enabled) this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); // Leaf - Async switch connection state CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); ServerConfigurationPacketListenerImpl serverConfigurationPacketListenerImpl = new ServerConfigurationPacketListenerImpl( this.server, this.connection, commonListenerCookie, this.player // CraftBukkit diff --git a/leaf-server/minecraft-patches/features/0254-Optimise-BlockEntities-tickersInLevel.patch b/leaf-server/minecraft-patches/features/0251-Optimize-BlockEntities-tickersInLevel.patch similarity index 81% rename from leaf-server/minecraft-patches/features/0254-Optimise-BlockEntities-tickersInLevel.patch rename to leaf-server/minecraft-patches/features/0251-Optimize-BlockEntities-tickersInLevel.patch index bd977680..392b7f90 100644 --- a/leaf-server/minecraft-patches/features/0254-Optimise-BlockEntities-tickersInLevel.patch +++ b/leaf-server/minecraft-patches/features/0251-Optimize-BlockEntities-tickersInLevel.patch @@ -1,11 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 8 May 2025 13:30:07 +0200 -Subject: [PATCH] Optimise BlockEntities tickersInLevel +Subject: [PATCH] Optimize BlockEntities tickersInLevel diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index e82e55ec400c5e338502ed7ce433372a88d4acff..155e9b807a61ab1212ee25cc79a386821596dedc 100644 +index e82e55ec400c5e338502ed7ce433372a88d4acff..31f19dfe16e270b55f3b44754c97ed8d9fa422cf 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -73,7 +73,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -13,7 +13,7 @@ index e82e55ec400c5e338502ed7ce433372a88d4acff..155e9b807a61ab1212ee25cc79a38682 } }; - private final Map tickersInLevel = Maps.newHashMap(); -+ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimiseBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); // Leaf - Optimise BlockEntities tickersInLevel ++ private final Map tickersInLevel = org.dreeam.leaf.config.modules.opt.OptimizeBlockEntities.enabled ? new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>() : Maps.newHashMap(); // Leaf - Optimise BlockEntities tickersInLevel public boolean loaded; public final ServerLevel level; // CraftBukkit - type @Nullable diff --git a/leaf-server/minecraft-patches/features/0255-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch b/leaf-server/minecraft-patches/features/0252-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0255-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch rename to leaf-server/minecraft-patches/features/0252-Pluto-Check-if-the-cactus-can-even-survive-being-pla.patch diff --git a/leaf-server/minecraft-patches/features/0256-Flush-location-while-knockback.patch b/leaf-server/minecraft-patches/features/0253-Flush-location-while-knockback.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0256-Flush-location-while-knockback.patch rename to leaf-server/minecraft-patches/features/0253-Flush-location-while-knockback.patch diff --git a/leaf-server/minecraft-patches/features/0257-Only-tick-items-at-hand.patch b/leaf-server/minecraft-patches/features/0254-Only-tick-items-at-hand.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0257-Only-tick-items-at-hand.patch rename to leaf-server/minecraft-patches/features/0254-Only-tick-items-at-hand.patch index 97a02640..51a0d8d8 100644 --- a/leaf-server/minecraft-patches/features/0257-Only-tick-items-at-hand.patch +++ b/leaf-server/minecraft-patches/features/0254-Only-tick-items-at-hand.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Only tick items at hand diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 59a61ac969e79966484a839b7d9ca0ac50e80994..82ac99c3c5b374482f3ff18f5d20bf888091e539 100644 +index 7abc297d91d6e1b21b855424b345dba4f4df37b6..0c81dca5905c2d4c166661dc3473b0c1e9aa5265 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -834,12 +834,19 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0258-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0255-Smart-sort-items-in-NearestItemSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0258-Smart-sort-items-in-NearestItemSensor.patch rename to leaf-server/minecraft-patches/features/0255-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0259-Optimise-player-movement-checks.patch b/leaf-server/minecraft-patches/features/0256-Optimise-player-movement-checks.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0259-Optimise-player-movement-checks.patch rename to leaf-server/minecraft-patches/features/0256-Optimise-player-movement-checks.patch diff --git a/leaf-server/minecraft-patches/features/0260-Remove-streams-in-MobSensor.patch b/leaf-server/minecraft-patches/features/0257-Remove-streams-in-MobSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0260-Remove-streams-in-MobSensor.patch rename to leaf-server/minecraft-patches/features/0257-Remove-streams-in-MobSensor.patch diff --git a/leaf-server/minecraft-patches/features/0261-Remove-streams-in-TemptingSensor.patch b/leaf-server/minecraft-patches/features/0258-Remove-streams-in-TemptingSensor.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0261-Remove-streams-in-TemptingSensor.patch rename to leaf-server/minecraft-patches/features/0258-Remove-streams-in-TemptingSensor.patch diff --git a/leaf-server/minecraft-patches/features/0262-Use-HashedList-on-WeightedList.patch b/leaf-server/minecraft-patches/features/0259-Use-HashedList-on-WeightedList.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0262-Use-HashedList-on-WeightedList.patch rename to leaf-server/minecraft-patches/features/0259-Use-HashedList-on-WeightedList.patch diff --git a/leaf-server/minecraft-patches/features/0263-Add-configurable-death-item-drop-knockback-settings.patch b/leaf-server/minecraft-patches/features/0260-Add-configurable-death-item-drop-knockback-settings.patch similarity index 96% rename from leaf-server/minecraft-patches/features/0263-Add-configurable-death-item-drop-knockback-settings.patch rename to leaf-server/minecraft-patches/features/0260-Add-configurable-death-item-drop-knockback-settings.patch index fd5f99d7..034c7c7b 100644 --- a/leaf-server/minecraft-patches/features/0263-Add-configurable-death-item-drop-knockback-settings.patch +++ b/leaf-server/minecraft-patches/features/0260-Add-configurable-death-item-drop-knockback-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add configurable death item drop knockback settings diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 82ac99c3c5b374482f3ff18f5d20bf888091e539..c9fbbbef8d006a90023c70419b5be7220602ee3b 100644 +index 0c81dca5905c2d4c166661dc3473b0c1e9aa5265..373059fcccdab0a918aa1d1a4a613fb62fef8a18 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -1053,7 +1053,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc diff --git a/leaf-server/minecraft-patches/features/0264-Optimize-getScaledTrackingDistance.patch b/leaf-server/minecraft-patches/features/0261-Optimize-getScaledTrackingDistance.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0264-Optimize-getScaledTrackingDistance.patch rename to leaf-server/minecraft-patches/features/0261-Optimize-getScaledTrackingDistance.patch diff --git a/leaf-server/minecraft-patches/features/0265-Optimize-SynchedEntityData-packDirty.patch b/leaf-server/minecraft-patches/features/0262-Optimize-SynchedEntityData-packDirty.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0265-Optimize-SynchedEntityData-packDirty.patch rename to leaf-server/minecraft-patches/features/0262-Optimize-SynchedEntityData-packDirty.patch diff --git a/leaf-server/minecraft-patches/features/0266-Optimize-isEyeInFluid.patch b/leaf-server/minecraft-patches/features/0263-Optimize-isEyeInFluid.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0266-Optimize-isEyeInFluid.patch rename to leaf-server/minecraft-patches/features/0263-Optimize-isEyeInFluid.patch diff --git a/leaf-server/minecraft-patches/features/0268-Cache-block-path-type.patch b/leaf-server/minecraft-patches/features/0264-Cache-block-path-type.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0268-Cache-block-path-type.patch rename to leaf-server/minecraft-patches/features/0264-Cache-block-path-type.patch diff --git a/leaf-server/minecraft-patches/features/0269-optimize-getEntityStatus.patch b/leaf-server/minecraft-patches/features/0265-optimize-getEntityStatus.patch similarity index 83% rename from leaf-server/minecraft-patches/features/0269-optimize-getEntityStatus.patch rename to leaf-server/minecraft-patches/features/0265-optimize-getEntityStatus.patch index cb937cc5..b32c3afa 100644 --- a/leaf-server/minecraft-patches/features/0269-optimize-getEntityStatus.patch +++ b/leaf-server/minecraft-patches/features/0265-optimize-getEntityStatus.patch @@ -5,7 +5,7 @@ Subject: [PATCH] optimize getEntityStatus diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -index 2d24d03bbdb5ee0d862cbfff2219f58afffafe12..11f4f2f3bc4c9f9f6a1f83b90f3de34c8edb9819 100644 +index 2d24d03bbdb5ee0d862cbfff2219f58afffafe12..703bf9c2a56b262e2719a1787584de537b8f12e0 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java @@ -93,8 +93,14 @@ public abstract class EntityLookup implements LevelEntityGetter { @@ -14,14 +14,14 @@ index 2d24d03bbdb5ee0d862cbfff2219f58afffafe12..11f4f2f3bc4c9f9f6a1f83b90f3de34c } - final Visibility visibility = EntityLookup.getEntityStatus(entity); - return visibility.isAccessible() ? entity : null; -+ // Leaf start ++ // Leaf start - optimize getEntityStatus + final FullChunkStatus entityStatus = ((ChunkSystemEntity) entity).moonrise$getChunkStatus(); + return switch (entityStatus) { + case INACCESSIBLE -> null; + case FULL, BLOCK_TICKING, ENTITY_TICKING -> entity; + case null -> null; + }; -+ // Leaf end ++ // Leaf end - optimize getEntityStatus } @Override @@ -30,26 +30,26 @@ index 2d24d03bbdb5ee0d862cbfff2219f58afffafe12..11f4f2f3bc4c9f9f6a1f83b90f3de34c } final FullChunkStatus entityStatus = ((ChunkSystemEntity)entity).moonrise$getChunkStatus(); - return Visibility.fromFullChunkStatus(entityStatus == null ? FullChunkStatus.INACCESSIBLE : entityStatus); -+ // Leaf start ++ // Leaf start - optimize getEntityStatus + return switch (entityStatus) { + case INACCESSIBLE -> Visibility.HIDDEN; + case FULL, BLOCK_TICKING -> Visibility.TRACKED; + case ENTITY_TICKING -> Visibility.TICKING; + case null -> Visibility.HIDDEN; + }; -+ // Leaf end ++ // Leaf end - optimize getEntityStatus } protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) { diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 5fda5eadd37ec52ca6470d4aeb18a20c192811be..35fdc9863598755ece1dcc53b4b42c0d5d5e882d 100644 +index e1a722c3ec74c0967d089ad469d7b3305bf66195..74794efea55ce1874730c8a2de5fdd9277065d10 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -374,6 +374,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // Paper end // Paper start - rewrite chunk system private final boolean isHardColliding = this.moonrise$isHardCollidingUncached(); -+ @org.jetbrains.annotations.Nullable // Leaf ++ @org.jetbrains.annotations.Nullable // Leaf - optimize getEntityStatus private net.minecraft.server.level.FullChunkStatus chunkStatus; private ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData chunkData; private int sectionX = Integer.MIN_VALUE; @@ -57,7 +57,7 @@ index 5fda5eadd37ec52ca6470d4aeb18a20c192811be..35fdc9863598755ece1dcc53b4b42c0d } @Override -+ @org.jetbrains.annotations.Nullable // Leaf ++ @org.jetbrains.annotations.Nullable // Leaf - optimize getEntityStatus public final net.minecraft.server.level.FullChunkStatus moonrise$getChunkStatus() { return this.chunkStatus; } diff --git a/leaf-server/minecraft-patches/features/0270-Paper-Rewrite-dataconverter-system.patch b/leaf-server/minecraft-patches/features/0266-Paper-Rewrite-dataconverter-system.patch similarity index 99% rename from leaf-server/minecraft-patches/features/0270-Paper-Rewrite-dataconverter-system.patch rename to leaf-server/minecraft-patches/features/0266-Paper-Rewrite-dataconverter-system.patch index 2ea6cf23..bd0c9a98 100644 --- a/leaf-server/minecraft-patches/features/0270-Paper-Rewrite-dataconverter-system.patch +++ b/leaf-server/minecraft-patches/features/0266-Paper-Rewrite-dataconverter-system.patch @@ -32327,7 +32327,7 @@ index 175647d57e59e838ea7b4680fbf22c161100c513..a7b772cfc89161414469cd7da374d6c7 return structureTemplate.save(new CompoundTag()); } diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 84b90f496e5ab59d43503301fe935627af123e3f..54e31a239d6563abe56435ee8de03e55548ead29 100644 +index 54f5336b5bb56cb9dfda2b75fff07bae0b27cfd3..60ec95c72506afe5cc58c15fef8749f96222c318 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -294,6 +294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -Date: Thu, 24 Apr 2025 16:36:16 -0400 -Subject: [PATCH] Paw optimization - -Some random optimizations - -- Remove Paper's dead code -- Only set shuffle random seed if is really used -- Secret patches (WIP) - -diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index c83ee2137a57e62003b1d20c3ceea9f569350a53..de1f271d36c7daa10c398e146386b51e2622df9a 100644 ---- a/net/minecraft/network/Connection.java -+++ b/net/minecraft/network/Connection.java -@@ -660,13 +660,7 @@ public class Connection extends SimpleChannelInboundHandler> { - if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) - || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING - || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { -- // Paper start - detailed watchdog information -- net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener); -- try { - tickablePacketListener.tick(); -- } finally { -- net.minecraft.network.protocol.PacketUtils.packetProcessing.pop(); -- } // Paper end - detailed watchdog information - } // Paper end - Buffer joins to world - } - -diff --git a/net/minecraft/network/protocol/PacketUtils.java b/net/minecraft/network/protocol/PacketUtils.java -index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194c783a2f4 100644 ---- a/net/minecraft/network/protocol/PacketUtils.java -+++ b/net/minecraft/network/protocol/PacketUtils.java -@@ -21,8 +21,6 @@ public class PacketUtils { - public static void ensureRunningOnSameThread(Packet packet, T processor, BlockableEventLoop executor) throws RunningOnDifferentThreadException { - if (!executor.isSameThread()) { - executor.executeIfPossible(() -> { -- packetProcessing.push(processor); // Paper - detailed watchdog information -- try { // Paper - detailed watchdog information - if (processor instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // Paper - Don't handle sync packets for kicked players - if (processor.shouldHandleMessage(packet)) { - try { -@@ -37,12 +35,6 @@ public class PacketUtils { - } else { - LOGGER.debug("Ignoring packet due to disconnection: {}", packet); - } -- // Paper start - detailed watchdog information -- } finally { -- totalMainThreadPacketsProcessed.getAndIncrement(); -- packetProcessing.pop(); -- } -- // Paper end - detailed watchdog information - }); - throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD; - } -@@ -69,22 +61,4 @@ public class PacketUtils { - - packetListener.fillCrashReport(crashReport); - } -- -- // Paper start - detailed watchdog information -- public static final java.util.concurrent.ConcurrentLinkedDeque packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>(); -- static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong(); -- -- public static long getTotalProcessedPackets() { -- return totalMainThreadPacketsProcessed.get(); -- } -- -- public static java.util.List getCurrentPacketProcessors() { -- java.util.List listeners = new java.util.ArrayList<>(4); -- for (PacketListener listener : packetProcessing) { -- listeners.add(listener); -- } -- -- return listeners; -- } -- // Paper end - detailed watchdog information - } -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index b3acfc7e00797ea16cc2c1793452f3ed97b9c61a..17c1b05f520145b84e90fc359dca4523191f08cd 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -622,8 +622,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - try { - this.chunkMap.collectSpawningChunks(list); - // Paper start - chunk tick iteration optimisation -- this.shuffleRandom.setSeed(this.level.random.nextLong()); -- if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled -+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used -+ Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled -+ } - // Paper end - chunk tick iteration optimisation - - for (LevelChunk levelChunk : list) { -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index c0044f4013520fd617ec365012b10862571744f3..14d23006d3ec15bb3ec6f976bff6c0975662c69d 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -1518,13 +1518,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - // Paper end - log detailed entity tick information - - public void tickNonPassenger(Entity entity) { -- // Paper start - log detailed entity tick information - ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main"); -- try { -- if (currentlyTickingEntity.get() == null) { -- currentlyTickingEntity.lazySet(entity); -- } -- // Paper end - log detailed entity tick information - entity.setOldPosAndRot(); - entity.tickCount++; - entity.totalEntityAge++; // Paper - age-like counter for all entities -@@ -1537,13 +1531,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - for (Entity entity1 : entity.getPassengers()) { - this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 - } -- // Paper start - log detailed entity tick information -- } finally { -- if (currentlyTickingEntity.get() == entity) { -- currentlyTickingEntity.lazySet(null); -- } -- } -- // Paper end - log detailed entity tick information - } - - private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 -diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index e1a722c3ec74c0967d089ad469d7b3305bf66195..5fda5eadd37ec52ca6470d4aeb18a20c192811be 100644 ---- a/net/minecraft/world/entity/Entity.java -+++ b/net/minecraft/world/entity/Entity.java -@@ -1144,16 +1144,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return this.onGround; - } - -- // Paper start - detailed watchdog information -- public final Object posLock = new Object(); // Paper - log detailed entity tick information -- -- @Nullable -- private Vec3 moveVector; -- private double moveStartX; -- private double moveStartY; -- private double moveStartZ; -- // Paper end - detailed watchdog information -- - public void move(MoverType type, Vec3 movement) { - // Gale start - VMP - skip entity move if movement is zero - if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { -@@ -1161,16 +1151,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - // Gale end - VMP - skip entity move if movement is zero - final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity -- // Paper start - detailed watchdog information - ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); -- synchronized (this.posLock) { -- this.moveStartX = this.getX(); -- this.moveStartY = this.getY(); -- this.moveStartZ = this.getZ(); -- this.moveVector = movement; -- } -- try { -- // Paper end - detailed watchdog information - if (this.noPhysics) { - this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); - } else { -@@ -1304,13 +1285,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - // Gale end - skip negligible planar movement multiplication - } - } -- // Paper start - detailed watchdog information -- } finally { -- synchronized (this.posLock) { // Paper -- this.moveVector = null; -- } // Paper -- } -- // Paper end - detailed watchdog information - } - - private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4862,9 +4836,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - public void setDeltaMovement(Vec3 deltaMovement) { -- synchronized (this.posLock) { // Paper - detailed watchdog information - this.deltaMovement = deltaMovement; -- } // Paper - detailed watchdog information - } - - public void addDeltaMovement(Vec3 addend) { -@@ -4972,9 +4944,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - // Paper end - Fix MC-4 - if (this.position.x != x || this.position.y != y || this.position.z != z) { -- synchronized (this.posLock) { // Paper - detailed watchdog information - this.position = new Vec3(x, y, z); -- } // Paper - detailed watchdog information - int floor = Mth.floor(x); - int floor1 = Mth.floor(y); - int floor2 = Mth.floor(z); -diff --git a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java -index ef9c2c0a665a1acf490affd9cd4496ae9d677410..27e7c1bb585f30165bd501bb8f8aab0dd147ca5b 100644 ---- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java -+++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java -@@ -48,7 +48,7 @@ public class DesertPyramidStructure extends SinglePieceStructure { - } - } - -- ObjectArrayList list = new ObjectArrayList<>(set.stream().toList()); -+ ObjectArrayList list = new ObjectArrayList<>(set); // Leaf - paw optimization - TODO: use array - RandomSource randomSource = RandomSource.create(level.getSeed()).forkPositional().at(pieces.calculateBoundingBox().getCenter()); - Util.shuffle(list, randomSource); - int min = Math.min(set.size(), randomSource.nextInt(5, 8)); diff --git a/leaf-server/minecraft-patches/features/0271-Rail-Optimization-optimized-PoweredRailBlock-logic.patch b/leaf-server/minecraft-patches/features/0267-Rail-Optimization-optimized-PoweredRailBlock-logic.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0271-Rail-Optimization-optimized-PoweredRailBlock-logic.patch rename to leaf-server/minecraft-patches/features/0267-Rail-Optimization-optimized-PoweredRailBlock-logic.patch diff --git a/leaf-server/minecraft-patches/features/0268-optimise-ChunkGenerator-getMobsAt.patch b/leaf-server/minecraft-patches/features/0268-optimise-ChunkGenerator-getMobsAt.patch new file mode 100644 index 00000000..e1bcee84 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0268-optimise-ChunkGenerator-getMobsAt.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:16:32 +0900 +Subject: [PATCH] optimise ChunkGenerator#getMobsAt + +inline fillStartsForStructure + +diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java +index 3399922d79a713484e16beb6e4e9985c284ddfb5..fbe93098ce0366054a6da857cd808af1431b6612 100644 +--- a/net/minecraft/world/level/StructureManager.java ++++ b/net/minecraft/world/level/StructureManager.java +@@ -78,7 +78,7 @@ public class StructureManager { + + public void fillStartsForStructure(Structure structure, LongSet structureRefs, Consumer startConsumer) { + for (long l : structureRefs) { +- SectionPos sectionPos = SectionPos.of(new ChunkPos(l), this.level.getMinSectionY()); ++ SectionPos sectionPos = SectionPos.of(ChunkPos.getX(l), this.level.getMinSectionY(), ChunkPos.getZ(l)); // Leaf - optimise ChunkGenerator#getMobsAt + StructureStart startForStructure = this.getStartForStructure( + sectionPos, structure, this.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_STARTS) + ); +@@ -178,8 +178,8 @@ public class StructureManager { + } + + public Map getAllStructuresAt(BlockPos pos) { +- SectionPos sectionPos = SectionPos.of(pos); +- return this.level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); ++ //SectionPos sectionPos = SectionPos.of(pos); // Leaf - optimise ChunkGenerator#getMobsAt ++ return this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); // Leaf - optimise ChunkGenerator#getMobsAt + } + + public StructureCheckResult checkStructurePresence(ChunkPos chunkPos, Structure structure, StructurePlacement placement, boolean skipKnownStructures) { +diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java +index e1ebbfab87aed9cc633f2fedb1e6edeca4ddc2ec..11c7c299d4affb9e78488590e7db939efe6e3dd9 100644 +--- a/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -496,18 +496,20 @@ public abstract class ChunkGenerator { + Structure structure = entry.getKey(); + StructureSpawnOverride structureSpawnOverride = structure.spawnOverrides().get(category); + if (structureSpawnOverride != null) { +- MutableBoolean mutableBoolean = new MutableBoolean(false); +- Predicate predicate = structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE +- ? structureStart -> structureManager.structureHasPieceAt(pos, structureStart) +- : structureStart -> structureStart.getBoundingBox().isInside(pos); +- structureManager.fillStartsForStructure(structure, entry.getValue(), structureStart -> { +- if (mutableBoolean.isFalse() && predicate.test(structureStart)) { +- mutableBoolean.setTrue(); ++ // Leaf start - optimise ChunkGenerator#getMobsAt ++ for (long l : entry.getValue()) { ++ StructureStart startForStructure = structureManager.getStartForStructure( ++ null, structure, structureManager.level.getChunk(ChunkPos.getX(l), ChunkPos.getZ(l), ChunkStatus.STRUCTURE_STARTS) ++ ); ++ if (startForStructure != null && startForStructure.isValid()) { ++ if (structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE ++ ? structureManager.structureHasPieceAt(pos, startForStructure) ++ : startForStructure.getBoundingBox().isInside(pos)) { ++ return structureSpawnOverride.spawns(); ++ } + } +- }); +- if (mutableBoolean.isTrue()) { +- return structureSpawnOverride.spawns(); + } ++ // Leaf end - optimise ChunkGenerator#getMobsAt + } + } + diff --git a/leaf-server/minecraft-patches/features/0269-optimise-getBiome.patch b/leaf-server/minecraft-patches/features/0269-optimise-getBiome.patch new file mode 100644 index 00000000..d52b0083 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0269-optimise-getBiome.patch @@ -0,0 +1,147 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:20:40 +0900 +Subject: [PATCH] optimise getBiome + + +diff --git a/net/minecraft/advancements/critereon/LocationPredicate.java b/net/minecraft/advancements/critereon/LocationPredicate.java +index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..65e4315cce35814c60b21bbd5baea2ffac82162c 100644 +--- a/net/minecraft/advancements/critereon/LocationPredicate.java ++++ b/net/minecraft/advancements/critereon/LocationPredicate.java +@@ -49,7 +49,7 @@ public record LocationPredicate( + } else { + BlockPos blockPos = BlockPos.containing(x, y, z); + boolean isLoaded = level.isLoaded(blockPos); +- return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiome(blockPos))) ++ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(org.dreeam.leaf.config.modules.opt.OptimizeBiome.advancement ? level.getBiomeCached(blockPos) : level.getBiome(blockPos))) // Leaf - cache getBiome + && (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid()) + && (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos)) + && (!this.light.isPresent() || this.light.get().matches(level, blockPos)) +diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java +index 0842fd6488c8b27d98c4344e1244996b4c0e9912..c6ddb49648c55443ae880c1adba9887ab0681c82 100644 +--- a/net/minecraft/world/level/LevelReader.java ++++ b/net/minecraft/world/level/LevelReader.java +@@ -57,6 +57,12 @@ public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_syste + return this.getBiomeManager().getBiome(pos); + } + ++ // Leaf start - cache getBiome ++ default Holder getBiomeCached(BlockPos pos) { ++ return this.getBiomeManager().getBiomeCached(pos); ++ } ++ // Leaf end - cache getBiome ++ + default Stream getBlockStatesIfLoaded(AABB aabb) { + int floor = Mth.floor(aabb.minX); + int floor1 = Mth.floor(aabb.maxX); +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index a2da4fce50f31d56036d04041c4f80ed90c18b27..f242941ce06d356a025e306efe78c688e9b755c4 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -443,7 +443,7 @@ public final class NaturalSpawner { + private static Optional getRandomSpawnMobAt( + ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos + ) { +- Holder biome = level.getBiome(pos); ++ Holder biome = org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos); // Leaf - cache getBiome + return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F + ? Optional.empty() + : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random); +@@ -460,7 +460,7 @@ public final class NaturalSpawner { + ) { + return isInNetherFortressBounds(pos, level, cetagory, structureManager) + ? NetherFortressStructure.FORTRESS_ENEMIES +- : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, cetagory, pos); ++ : generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos)), structureManager, cetagory, pos); // Leaf - cache getBiome + } + + public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { +diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java +index a48175a7ebb1788ace46395621ed78d910178a53..00122472991ba0c1a0ea77053aad71cdfa92a7bd 100644 +--- a/net/minecraft/world/level/biome/BiomeManager.java ++++ b/net/minecraft/world/level/biome/BiomeManager.java +@@ -15,10 +15,23 @@ public class BiomeManager { + private final BiomeManager.NoiseBiomeSource noiseBiomeSource; + private final long biomeZoomSeed; + private static final double maxOffset = 0.4500000001D; // Leaf - Carpet-Fixes - Optimized getBiome method ++ // Leaf start - cache getBiome ++ private final Holder[] biomeCache; ++ private final long[] biomeCachePos; ++ // Leaf end - cache getBiome + + public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { + this.noiseBiomeSource = noiseBiomeSource; + this.biomeZoomSeed = biomeZoomSeed; ++ // Leaf start - cache getBiome ++ if (org.dreeam.leaf.config.modules.opt.OptimizeBiome.enabled) { ++ biomeCache = new Holder[65536]; ++ biomeCachePos = new long[65536]; ++ } else { ++ biomeCache = null; ++ biomeCachePos = null; ++ } ++ // Leaf end - cache getBiome + } + + public static long obfuscateSeed(long seed) { +@@ -29,6 +42,40 @@ public class BiomeManager { + return new BiomeManager(newSource, this.biomeZoomSeed); + } + ++ // Leaf start - cache getBiome ++ public Holder getBiomeCached(BlockPos pos) { ++ if (biomeCache == null) { ++ return getBiome(pos); ++ } ++ int xMinus2 = pos.getX() - 2; ++ int yMinus2 = pos.getY() - 2; ++ int zMinus2 = pos.getZ() - 2; ++ int x = xMinus2 >> 2; ++ int y = yMinus2 >> 2; ++ int z = zMinus2 >> 2; ++ long packedPos = BlockPos.asLong(x, y, z); ++ long hash = packedPos; ++ hash = (hash ^ (hash >>> 32)) * 0xff51afd7ed558ccdL; ++ hash = (hash ^ (hash >>> 32)) * 0xc4ceb9fe1a85ec53L; ++ hash = (hash ^ (hash >>> 32)) & 65535L; ++ ++ long pos1 = biomeCachePos[(int) hash]; ++ if (pos1 == packedPos) { ++ Holder biome = biomeCache[(int) hash]; ++ if (biome != null) { ++ return biome; ++ } ++ } ++ ++ Holder biome = getBiome(pos); ++ ++ biomeCache[(int) hash] = biome; ++ biomeCachePos[(int) hash] = packedPos; ++ ++ return biome; ++ } ++ // Leaf end - cache getBiome ++ + public Holder getBiome(BlockPos pos) { + // Leaf start - Carpet-Fixes - Optimized getBiome method + int xMinus2 = pos.getX() - 2; +@@ -126,9 +173,18 @@ public class BiomeManager { + return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle); + } + ++ // Leaf start - optimise getBiome ++ private static final double[] FIDDLE_TABLE = new double[1024]; ++ static { ++ for (int i = 0; i < 1024; i++) { ++ FIDDLE_TABLE[i] = (i - 512) * (0.9 / 1024.0); ++ } ++ } + private static double getFiddle(long seed) { +- return (double)(((seed >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction ++ return FIDDLE_TABLE[(int)(seed >>> 24) & 1023]; ++ //return (double)(((seed >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction + } ++ // Leaf end - optimise getBiome + + public interface NoiseBiomeSource { + Holder getNoiseBiome(int x, int y, int z); diff --git a/leaf-server/minecraft-patches/features/0270-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0270-optimize-mob-spawning.patch new file mode 100644 index 00000000..c8b32c9d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0270-optimize-mob-spawning.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:20:59 +0900 +Subject: [PATCH] optimize mob spawning + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index f242941ce06d356a025e306efe78c688e9b755c4..38a6a11c05c0a0a2f59c1477e516431263dce101 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -472,6 +472,17 @@ public final class NaturalSpawner { + } + } + ++ // Leaf start - optimize mob spawning ++ private static void mutableRandomPosWithin(BlockPos.MutableBlockPos pos1, Level level, LevelChunk chunk) { ++ ChunkPos pos = chunk.getPos(); ++ int randomX = pos.getMinBlockX() + level.random.nextInt(16); ++ int randomZ = pos.getMinBlockZ() + level.random.nextInt(16); ++ int surfaceY = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, randomX, randomZ) + 1; ++ int randomY = Mth.randomBetweenInclusive(level.random, level.getMinY(), surfaceY); ++ pos1.set(randomX, randomY, randomZ); ++ } ++ // Leaf end - optimize mob spawning ++ + private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) { + ChunkPos pos = chunk.getPos(); + int i = pos.getMinBlockX() + level.random.nextInt(16); diff --git a/leaf-server/minecraft-patches/features/0271-optimize-structure-map.patch b/leaf-server/minecraft-patches/features/0271-optimize-structure-map.patch new file mode 100644 index 00000000..ee709fcd --- /dev/null +++ b/leaf-server/minecraft-patches/features/0271-optimize-structure-map.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 15:41:12 +0900 +Subject: [PATCH] optimize structure map + + +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index 4fd9313ce2c87383685d80e2533b93d5b85a9f41..c9655ab6faf33fca2971629cd819f66da7024240 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -76,8 +76,8 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + protected BlendingData blendingData; + public final Map heightmaps = Maps.newEnumMap(Heightmap.Types.class); + // Paper - rewrite chunk system +- private final Map structureStarts = Maps.newHashMap(); +- private final Map structuresRefences = Maps.newHashMap(); ++ private final Map structureStarts = new it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap<>(); // Leaf - optimize structure map ++ private final Map structuresRefences = new it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap<>(); // Leaf - optimize structure map + protected final Map pendingBlockEntities = Maps.newHashMap(); + public final Map blockEntities = new Object2ObjectOpenHashMap<>(); + protected final LevelHeightAccessor levelHeightAccessor; +@@ -303,7 +303,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + } + + public Map getAllStarts() { +- return Collections.unmodifiableMap(this.structureStarts); ++ return this.structureStarts; // Leaf - optimize structure map + } + + public void setAllStarts(Map structureStarts) { +@@ -319,13 +319,13 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + + @Override + public void addReferenceForStructure(Structure structure, long reference) { +- this.structuresRefences.computeIfAbsent(structure, key -> new LongOpenHashSet()).add(reference); ++ this.structuresRefences.computeIfAbsent(structure, key -> new it.unimi.dsi.fastutil.longs.LongArraySet()).add(reference); // Leaf - optimize structure map + this.markUnsaved(); + } + + @Override + public Map getAllReferences() { +- return Collections.unmodifiableMap(this.structuresRefences); ++ return this.structuresRefences; // Leaf - optimize structure map + } + + @Override diff --git a/leaf-server/minecraft-patches/features/0272-throttle-mob-spawning.patch b/leaf-server/minecraft-patches/features/0272-throttle-mob-spawning.patch new file mode 100644 index 00000000..b5d60f04 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0272-throttle-mob-spawning.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Wed, 4 Jun 2025 20:54:03 +0900 +Subject: [PATCH] throttle mob spawning + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index 38a6a11c05c0a0a2f59c1477e516431263dce101..a55abb08093847a8bfec446659f9af5fb1df2824 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -154,6 +154,17 @@ public final class NaturalSpawner { + // Paper start - Optional per player mob spawns + final boolean canSpawn; + int maxSpawns = Integer.MAX_VALUE; ++ // Leaf start - throttle mob spawning ++ if (org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.enabled) { ++ int spawnChance = org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.spawnChance[mobCategory.ordinal()]; ++ long failedAttempt = org.dreeam.leaf.config.modules.opt.ThrottleNaturalMobSpawning.failedAttempts[mobCategory.ordinal()]; ++ if (failedAttempt >= 0L ++ && chunk.failedSpawnAttempts[mobCategory.ordinal()] >= failedAttempt ++ && (level.random.nextInt() & 1023) > spawnChance) { ++ continue; ++ } ++ } ++ // Leaf end - throttle mob spawning + if (level.paperConfig().entities.spawning.perPlayerMobSpawns) { + // Copied from getFilteredSpawningCategories + int limit = mobCategory.getMaxInstancesPerChunk(); diff --git a/leaf-server/minecraft-patches/features/0273-preload-mob-spawning-position.patch b/leaf-server/minecraft-patches/features/0273-preload-mob-spawning-position.patch new file mode 100644 index 00000000..a3da41d9 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0273-preload-mob-spawning-position.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Wed, 4 Jun 2025 20:54:32 +0900 +Subject: [PATCH] preload mob spawning position + + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index a55abb08093847a8bfec446659f9af5fb1df2824..555983c7fa7b1f2d72ebbc148ca3dcb7cd217c56 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -243,12 +243,61 @@ public final class NaturalSpawner { + // Paper end - throttle failed spawn attempts + ) { + // Paper end - Optional per player mob spawns +- BlockPos randomPosWithin = getRandomPosWithin(level, chunk); +- if (randomPosWithin.getY() >= level.getMinY() + 1) { +- return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts +- } ++ // Leaf start - preload mob spawning position ++ BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + +- return 0; // Paper - throttle failed spawn attempts ++ if (org.dreeam.leaf.config.modules.opt.PreloadNaturalMobSpawning.enabled) { ++ if (chunk.cacheSpawnPosIndex == 16 || chunk.cacheSpawnPosIndex == -1) { ++ if (chunk.cacheSpawnPos == null) { ++ chunk.cacheSpawnPos = new long[16]; ++ } ++ // cache friendly ++ for (int i = 0; i < 16; i++) { ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() >= level.getMinY() + 1 ++ && level.getWorldBorder().isWithinBounds(pos) ++ && !level.isOutsideBuildHeight(pos)) { ++ LevelChunk chunk1 = chunk.getPos().longKey == ChunkPos.asLong(pos) ++ ? chunk ++ : level.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); ++ if (chunk1 != null) { ++ BlockState bs = chunk1.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); ++ if (bs != null && !bs.isRedstoneConductor(level, pos)) { ++ chunk.cacheSpawnPos[i] = BlockPos.asLong(pos.getX(), pos.getY(), pos.getZ()); ++ continue; ++ } ++ } ++ } ++ chunk.cacheSpawnPos[i] = -1; ++ } ++ chunk.cacheSpawnPosIndex = 0; ++ } ++ long cachePos = chunk.cacheSpawnPos[chunk.cacheSpawnPosIndex]; ++ chunk.cacheSpawnPosIndex++; ++ if (cachePos == -1) { ++ return 0; ++ } ++ pos.set(cachePos); ++ } else { ++ mutableRandomPosWithin(pos, level, chunk); ++ if (pos.getY() < level.getMinY() + 1 ++ || !level.getWorldBorder().isWithinBounds(pos) ++ || level.isOutsideBuildHeight(pos)) { ++ return 0; ++ } ++ LevelChunk chunk1 = chunk.getPos().longKey == ChunkPos.asLong(pos) ++ ? chunk ++ : level.chunkSource.getChunkAtIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); ++ if (chunk1 == null) { ++ return 0; ++ } ++ BlockState bs = chunk1.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); ++ if (bs == null || bs.isRedstoneConductor(level, pos)) { ++ return 0; ++ } ++ } ++ return spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts ++ // Leaf end - preload mob spawning position + } + + @VisibleForDebug +@@ -270,7 +319,12 @@ public final class NaturalSpawner { + MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity + // Paper start - throttle failed spawn attempts + ) { +- spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); ++ // Leaf start - preload mob spawning position ++ BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); ++ if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { ++ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); ++ } ++ // Leaf end - preload mob spawning position + } + public static int spawnCategoryForPosition( + MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity, final boolean nothing +@@ -281,8 +335,8 @@ public final class NaturalSpawner { + ChunkGenerator generator = level.getChunkSource().getGenerator(); + int y = pos.getY(); + int i = 0; // Paper - throttle failed spawn attempts +- BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn +- if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn ++ //BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn // Leaf - preload mob spawning position ++ if (true /*blockState != null && !blockState.isRedstoneConductor(chunk, pos)*/) { // Paper - don't load chunks for mob spawn // Leaf - preload mob spawning position + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + //int i = 0; // Paper - throttle failed spawn attempts - move up + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 31f19dfe16e270b55f3b44754c97ed8d9fa422cf..b79eefb182b0a9e9f22ccb649c4659483228082c 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -107,6 +107,8 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + // Paper start - rewrite chunk system + private boolean postProcessingDone; + private ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkAndHolder; ++ public long[] cacheSpawnPos = null; // Leaf - preload mob spawning position ++ public int cacheSpawnPosIndex = -1; // Leaf - preload mob spawning position + + @Override + public final boolean moonrise$isPostProcessingDone() { diff --git a/leaf-server/minecraft-patches/features/0274-Add-BlockExplosionHitEvent.patch b/leaf-server/minecraft-patches/features/0274-Add-BlockExplosionHitEvent.patch new file mode 100644 index 00000000..4788ce7b --- /dev/null +++ b/leaf-server/minecraft-patches/features/0274-Add-BlockExplosionHitEvent.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Pascalpex +Date: Wed, 4 Jun 2025 17:03:32 +0200 +Subject: [PATCH] Add BlockExplosionHitEvent + + +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index 619d00f79276665777f3a58dec7a5f353bd7d660..33cd3a7755ada2fb632cfc97a80c8a9000ab0bd9 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -612,9 +612,13 @@ public class ServerExplosion implements Explosion { + } + // CraftBukkit end + +- this.level +- .getBlockState(blockPos) +- .onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1)); ++ // Leaf start - Add BlockExplosionHitEvent ++ if (new org.dreeam.leaf.event.BlockExplosionHitEvent(CraftLocation.toBukkit(blockPos, this.level.getWorld()).getBlock(), this.source == null ? null : this.source.getBukkitEntity(), org.bukkit.craftbukkit.CraftExplosionResult.toExplosionResult(this.blockInteraction)).callEvent()) { ++ this.level ++ .getBlockState(blockPos) ++ .onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1)); ++ } ++ // Leaf end - Add BlockExplosionHitEvent + } + + for (ServerExplosion.StackCollector stackCollector : list) { diff --git a/leaf-server/minecraft-patches/features/0275-Old-Blast-Protection-explosion-knockback.patch b/leaf-server/minecraft-patches/features/0275-Old-Blast-Protection-explosion-knockback.patch new file mode 100644 index 00000000..aa08cc6d --- /dev/null +++ b/leaf-server/minecraft-patches/features/0275-Old-Blast-Protection-explosion-knockback.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Mon, 2 Jun 2025 03:29:20 +0800 +Subject: [PATCH] Old Blast Protection explosion knockback + + +diff --git a/net/minecraft/world/entity/EquipmentSlot.java b/net/minecraft/world/entity/EquipmentSlot.java +index dbf31389f0e9796c80afbffddf6a20cbaf184e6e..f1b456bf96e4764fd202f5575bbfa58694c1c069 100644 +--- a/net/minecraft/world/entity/EquipmentSlot.java ++++ b/net/minecraft/world/entity/EquipmentSlot.java +@@ -23,6 +23,7 @@ public enum EquipmentSlot implements StringRepresentable { + // Gale start - JettPack - reduce array allocations + public static final EquipmentSlot[] VALUES_ARRAY = values(); + public static final List VALUES = List.of(VALUES_ARRAY); ++ public static final EquipmentSlot[] ARMOR_SLOTS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; // Leaf - Old Blast Protection explosion knockback + public static final IntFunction BY_ID = ByIdMap.continuous(equipmentSlot -> equipmentSlot.id, VALUES_ARRAY, ByIdMap.OutOfBoundsStrategy.ZERO); + public static final StringRepresentable.EnumCodec CODEC = StringRepresentable.fromEnum(() -> VALUES_ARRAY); + // Gale end - JettPack - reduce array allocations +diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java +index d73f58e7d65438c17a8ce11c5dbc9362145f152d..f9b957af7ca5e4511c76c0f8c138d142382993f3 100644 +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -529,7 +529,7 @@ public class ServerExplosion implements Explosion { + double d4 = (1.0 - d) * f1 * knockbackMultiplier; + double d5; + if (entity instanceof LivingEntity livingEntity) { +- d5 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : d4 * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); // Paper ++ d5 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : getExplosionKnockback(livingEntity, d4); // Paper // Leaf - Old Blast Protection explosion knockback + } else { + d5 = d4; + } +@@ -558,6 +558,49 @@ public class ServerExplosion implements Explosion { + } + } + ++ // Leaf start - Old Blast Protection explosion knockback ++ private static double getExplosionKnockback(LivingEntity entity, double velocity) { ++ if (!org.dreeam.leaf.config.modules.gameplay.Knockback.oldBlastProtectionKnockbackBehavior) { ++ return velocity * (1.0 - entity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); ++ } ++ ++ // Old BLAST_PROTECTION logic ++ // BLAST_PROTECTION used ARMOR_SLOTS for slot types ++ // See 1.20.4's ProtectionEnchantment#getExplosionKnockbackAfterDampener, ++ // EnchantmentHelper#getEnchantmentLevel, Enchantment#getSlotItems, ++ // EnchantmentHelper#getItemEnchantmentLevel, Enchantments#BLAST_PROTECTION, ++ // these methods/fields for reference. ++ Map map = com.google.common.collect.Maps.newEnumMap(net.minecraft.world.entity.EquipmentSlot.class); ++ ++ for (net.minecraft.world.entity.EquipmentSlot slot : net.minecraft.world.entity.EquipmentSlot.ARMOR_SLOTS) { ++ ItemStack itemStack = entity.getItemBySlot(slot); ++ if (!itemStack.isEmpty()) { ++ map.put(slot, itemStack); ++ } ++ } ++ ++ Iterable items = map.values(); ++ int i = 0; ++ ++ if (items == null) { ++ return 0; ++ } ++ ++ for (ItemStack itemStack : items) { ++ int enchantmentLevel = net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.BLAST_PROTECTION, itemStack); ++ if (enchantmentLevel > i) { ++ i = enchantmentLevel; ++ } ++ } ++ ++ if (i > 0) { ++ velocity *= Mth.clamp(1.0 - (double) i * 0.15, 0.0, 1.0); ++ } ++ ++ return velocity; ++ } ++ // Leaf end - Old Blast Protection explosion knockback ++ + private void interactWithBlocks(List blocks) { + List list = new ArrayList<>(); + Util.shuffle(blocks, this.level.random); diff --git a/leaf-server/minecraft-patches/features/0276-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0276-Paw-optimization.patch new file mode 100644 index 00000000..8a75e4e4 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0276-Paw-optimization.patch @@ -0,0 +1,542 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Thu, 24 Apr 2025 16:36:16 -0400 +Subject: [PATCH] Paw optimization + +Some random optimizations + +- Remove Paper's dead code +- Only set shuffle random seed if is really used +- Cache direction values to skip copy +- Secret patches (WIP) + +diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java +index 2491ca5bcabcf8ae99ef990eed4e1fd94f799991..0a5e4eadde52e368842d958b166e0070cf1d9345 100644 +--- a/net/minecraft/Util.java ++++ b/net/minecraft/Util.java +@@ -96,7 +96,7 @@ public class Util { + private static final TracingExecutor BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - Perf: add priority + private static final TracingExecutor IO_POOL = makeIoExecutor("IO-Worker-", false); + public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool +- private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); ++ //private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true); // Leaf - paw optimization - Remove client-only code + // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + // Leaf start - More virtual threads + public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { +@@ -270,7 +270,7 @@ public class Util { + } + + public static TracingExecutor nonCriticalIoPool() { +- return DOWNLOAD_POOL; ++ return null; // Leaf - paw optimization - Remove client-only code + } + + public static void shutdownExecutors() { +diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java +index 3dce0665e7438d2994a86450e31fb2a10431df9b..f9634a821fbfaf31a66e0e25973794149b1a5239 100644 +--- a/net/minecraft/network/Connection.java ++++ b/net/minecraft/network/Connection.java +@@ -654,13 +654,7 @@ public class Connection extends SimpleChannelInboundHandler> { + if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) + || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING + || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { +- // Paper start - detailed watchdog information +- net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener); +- try { + tickablePacketListener.tick(); +- } finally { +- net.minecraft.network.protocol.PacketUtils.packetProcessing.pop(); +- } // Paper end - detailed watchdog information + } // Paper end - Buffer joins to world + } + +diff --git a/net/minecraft/network/protocol/PacketUtils.java b/net/minecraft/network/protocol/PacketUtils.java +index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194c783a2f4 100644 +--- a/net/minecraft/network/protocol/PacketUtils.java ++++ b/net/minecraft/network/protocol/PacketUtils.java +@@ -21,8 +21,6 @@ public class PacketUtils { + public static void ensureRunningOnSameThread(Packet packet, T processor, BlockableEventLoop executor) throws RunningOnDifferentThreadException { + if (!executor.isSameThread()) { + executor.executeIfPossible(() -> { +- packetProcessing.push(processor); // Paper - detailed watchdog information +- try { // Paper - detailed watchdog information + if (processor instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // Paper - Don't handle sync packets for kicked players + if (processor.shouldHandleMessage(packet)) { + try { +@@ -37,12 +35,6 @@ public class PacketUtils { + } else { + LOGGER.debug("Ignoring packet due to disconnection: {}", packet); + } +- // Paper start - detailed watchdog information +- } finally { +- totalMainThreadPacketsProcessed.getAndIncrement(); +- packetProcessing.pop(); +- } +- // Paper end - detailed watchdog information + }); + throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD; + } +@@ -69,22 +61,4 @@ public class PacketUtils { + + packetListener.fillCrashReport(crashReport); + } +- +- // Paper start - detailed watchdog information +- public static final java.util.concurrent.ConcurrentLinkedDeque packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>(); +- static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong(); +- +- public static long getTotalProcessedPackets() { +- return totalMainThreadPacketsProcessed.get(); +- } +- +- public static java.util.List getCurrentPacketProcessors() { +- java.util.List listeners = new java.util.ArrayList<>(4); +- for (PacketListener listener : packetProcessing) { +- listeners.add(listener); +- } +- +- return listeners; +- } +- // Paper end - detailed watchdog information + } +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 9dd6205e1cdd2124ab9d91f0a1e344eb6aa1fb2e..d8ae228fa4dfb265b628e83936f297f88c74aa57 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -622,8 +622,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + try { + this.chunkMap.collectSpawningChunks(list); + // Paper start - chunk tick iteration optimisation +- this.shuffleRandom.setSeed(this.level.random.nextLong()); +- if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled ++ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used ++ Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled ++ } + // Paper end - chunk tick iteration optimisation + + for (LevelChunk levelChunk : list) { +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index c0044f4013520fd617ec365012b10862571744f3..14d23006d3ec15bb3ec6f976bff6c0975662c69d 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -1518,13 +1518,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Paper end - log detailed entity tick information + + public void tickNonPassenger(Entity entity) { +- // Paper start - log detailed entity tick information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main"); +- try { +- if (currentlyTickingEntity.get() == null) { +- currentlyTickingEntity.lazySet(entity); +- } +- // Paper end - log detailed entity tick information + entity.setOldPosAndRot(); + entity.tickCount++; + entity.totalEntityAge++; // Paper - age-like counter for all entities +@@ -1537,13 +1531,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + for (Entity entity1 : entity.getPassengers()) { + this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 + } +- // Paper start - log detailed entity tick information +- } finally { +- if (currentlyTickingEntity.get() == entity) { +- currentlyTickingEntity.lazySet(null); +- } +- } +- // Paper end - log detailed entity tick information + } + + private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 74794efea55ce1874730c8a2de5fdd9277065d10..834ee8d360cbfb044ac7405c49f952c1ec43b64d 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -1146,16 +1146,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return this.onGround; + } + +- // Paper start - detailed watchdog information +- public final Object posLock = new Object(); // Paper - log detailed entity tick information +- +- @Nullable +- private Vec3 moveVector; +- private double moveStartX; +- private double moveStartY; +- private double moveStartZ; +- // Paper end - detailed watchdog information +- + public void move(MoverType type, Vec3 movement) { + // Gale start - VMP - skip entity move if movement is zero + if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { +@@ -1163,16 +1153,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + // Gale end - VMP - skip entity move if movement is zero + final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity +- // Paper start - detailed watchdog information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); +- synchronized (this.posLock) { +- this.moveStartX = this.getX(); +- this.moveStartY = this.getY(); +- this.moveStartZ = this.getZ(); +- this.moveVector = movement; +- } +- try { +- // Paper end - detailed watchdog information + if (this.noPhysics) { + this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); + } else { +@@ -1306,13 +1287,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // Gale end - skip negligible planar movement multiplication + } + } +- // Paper start - detailed watchdog information +- } finally { +- synchronized (this.posLock) { // Paper +- this.moveVector = null; +- } // Paper +- } +- // Paper end - detailed watchdog information + } + + private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { +@@ -4864,9 +4838,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void setDeltaMovement(Vec3 deltaMovement) { +- synchronized (this.posLock) { // Paper - detailed watchdog information + this.deltaMovement = deltaMovement; +- } // Paper - detailed watchdog information + } + + public void addDeltaMovement(Vec3 addend) { +@@ -4974,9 +4946,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + // Paper end - Fix MC-4 + if (this.position.x != x || this.position.y != y || this.position.z != z) { +- synchronized (this.posLock) { // Paper - detailed watchdog information + this.position = new Vec3(x, y, z); +- } // Paper - detailed watchdog information + int floor = Mth.floor(x); + int floor1 = Mth.floor(y); + int floor2 = Mth.floor(z); +diff --git a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java +index 2653ae5bf66f2b117f86e4df04d9cc307ba09011..0278e54171609b9baa8999d321f47849a6e62155 100644 +--- a/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java ++++ b/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java +@@ -43,7 +43,7 @@ public abstract class BaseCoralPlantTypeBlock extends Block implements SimpleWat + if (state.getValue(WATERLOGGED)) { + return true; + } else { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (level.getFluidState(pos.relative(direction)).is(FluidTags.WATER)) { + return true; + } +diff --git a/net/minecraft/world/level/block/BaseFireBlock.java b/net/minecraft/world/level/block/BaseFireBlock.java +index 111eb3a1c584ae42e1afb784cfbaa50dc252e91f..be0e229a0b0b531d956cbfcb8184df644f470fde 100644 +--- a/net/minecraft/world/level/block/BaseFireBlock.java ++++ b/net/minecraft/world/level/block/BaseFireBlock.java +@@ -206,7 +206,7 @@ public abstract class BaseFireBlock extends Block { + BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); + boolean flag = false; + +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (level.getBlockState(mutableBlockPos.set(pos).move(direction1)).is(Blocks.OBSIDIAN)) { + flag = true; + break; +diff --git a/net/minecraft/world/level/block/FireBlock.java b/net/minecraft/world/level/block/FireBlock.java +index c03ad7be16fbb9f98c28bc09de59bf33d4edf5d7..9fea651771107504fee8ccb1179f454b51d477b8 100644 +--- a/net/minecraft/world/level/block/FireBlock.java ++++ b/net/minecraft/world/level/block/FireBlock.java +@@ -138,7 +138,7 @@ public class FireBlock extends BaseFireBlock { + if (!this.canBurn(blockState) && !blockState.isFaceSturdy(level, blockPos, Direction.UP)) { + BlockState blockState1 = this.defaultBlockState(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BooleanProperty booleanProperty = PROPERTY_BY_DIRECTION.get(direction); + if (booleanProperty != null) { + blockState1 = blockState1.setValue(booleanProperty, this.canBurn(level.getBlockState(pos.relative(direction)))); +@@ -310,7 +310,7 @@ public class FireBlock extends BaseFireBlock { + } + + private boolean isValidFireLocation(BlockGetter level, BlockPos pos) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (this.canBurn(level.getBlockState(pos.relative(direction)))) { + return true; + } +@@ -325,7 +325,7 @@ public class FireBlock extends BaseFireBlock { + } else { + int i = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockState blockState = level.getBlockState(pos.relative(direction)); + i = Math.max(this.getIgniteOdds(blockState), i); + } +diff --git a/net/minecraft/world/level/block/FrostedIceBlock.java b/net/minecraft/world/level/block/FrostedIceBlock.java +index 7001d7d488a5f2e8a39e30866b1b93a38807ddee..ff602b8f03884b3fa1b9aa7aecf5709356abbf41 100644 +--- a/net/minecraft/world/level/block/FrostedIceBlock.java ++++ b/net/minecraft/world/level/block/FrostedIceBlock.java +@@ -48,7 +48,7 @@ public class FrostedIceBlock extends IceBlock { + && this.slightlyMelt(state, level, pos)) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + BlockState blockState = level.getBlockState(mutableBlockPos); + if (blockState.is(this) && !this.slightlyMelt(blockState, level, mutableBlockPos)) { +@@ -84,7 +84,7 @@ public class FrostedIceBlock extends IceBlock { + int i = 0; + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + if (level.getBlockState(mutableBlockPos).is(this)) { + if (++i >= neighborsRequired) { +diff --git a/net/minecraft/world/level/block/LeavesBlock.java b/net/minecraft/world/level/block/LeavesBlock.java +index 010e9814490ffaa153df5b7865da17e2a84c7e82..d6750aec0f4695a99557beb92fda7a892fecc58d 100644 +--- a/net/minecraft/world/level/block/LeavesBlock.java ++++ b/net/minecraft/world/level/block/LeavesBlock.java +@@ -110,7 +110,7 @@ public abstract class LeavesBlock extends Block implements SimpleWaterloggedBloc + int i = 7; + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + i = Math.min(i, getDistanceAt(level.getBlockState(mutableBlockPos)) + 1); + if (i == 1) { +diff --git a/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +index 10484ee85482e86be5e0a09d8202df600a32092e..5b530b582d865ec407639c2262823bb7057b92a9 100644 +--- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java ++++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java +@@ -127,7 +127,7 @@ public class PistonBaseBlock extends DirectionalBlock { + } + + private boolean getNeighborSignal(SignalGetter signalGetter, BlockPos pos, Direction direction) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction1 != direction && signalGetter.hasSignal(pos.relative(direction1), direction1)) { + return true; + } +@@ -138,7 +138,7 @@ public class PistonBaseBlock extends DirectionalBlock { + } else { + BlockPos blockPos = pos.above(); + +- for (Direction direction2 : Direction.values()) { ++ for (Direction direction2 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction2 != Direction.DOWN && signalGetter.hasSignal(blockPos.relative(direction2), direction2)) { + return true; + } +diff --git a/net/minecraft/world/level/block/state/pattern/BlockPattern.java b/net/minecraft/world/level/block/state/pattern/BlockPattern.java +index f7bb979f08634a7e1b77c59040f59fb5e11aafa5..9b60bbebd32f045bd0f5b91cd30264396e673d30 100644 +--- a/net/minecraft/world/level/block/state/pattern/BlockPattern.java ++++ b/net/minecraft/world/level/block/state/pattern/BlockPattern.java +@@ -79,8 +79,8 @@ public class BlockPattern { + int max = Math.max(Math.max(this.width, this.height), this.depth); + + for (BlockPos blockPos : BlockPos.betweenClosed(pos, pos.offset(max - 1, max - 1, max - 1))) { +- for (Direction direction : Direction.values()) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction1 != direction && direction1 != direction.getOpposite()) { + BlockPattern.BlockPatternMatch blockPatternMatch = this.matches(blockPos, direction, direction1, loadingCache); + if (blockPatternMatch != null) { +diff --git a/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java b/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java +index c0940bea065d109a29dd4218881c117d517dcf67..9bf0a66115c1c64b036d1504d9842b49efeb5879 100644 +--- a/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java ++++ b/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java +@@ -256,7 +256,7 @@ public interface VibrationSystem { + Vec3 vec3 = new Vec3(Mth.floor(eventPos.x) + 0.5, Mth.floor(eventPos.y) + 0.5, Mth.floor(eventPos.z) + 0.5); + Vec3 vec31 = new Vec3(Mth.floor(vibrationUserPos.x) + 0.5, Mth.floor(vibrationUserPos.y) + 0.5, Mth.floor(vibrationUserPos.z) + 0.5); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + Vec3 vec32 = vec3.relative(direction, 1.0E-5F); + if (level.isBlockInLine(new ClipBlockStateContext(vec32, vec31, state -> state.is(BlockTags.OCCLUDES_VIBRATION_SIGNALS))).getType() + != HitResult.Type.BLOCK) { +diff --git a/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java b/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java +index 5bbc9cacd8675c96d99f8b46399742753888d2f7..0c818d43c3d58e1f61e30d63556ba82356d53290 100644 +--- a/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/BlueIceFeature.java +@@ -26,7 +26,7 @@ public class BlueIceFeature extends Feature { + } else { + boolean flag = false; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction != Direction.DOWN && worldGenLevel.getBlockState(blockPos.relative(direction)).is(Blocks.PACKED_ICE)) { + flag = true; + break; +@@ -51,7 +51,7 @@ public class BlueIceFeature extends Feature { + ); + BlockState blockState = worldGenLevel.getBlockState(blockPos1); + if (blockState.isAir() || blockState.is(Blocks.WATER) || blockState.is(Blocks.PACKED_ICE) || blockState.is(Blocks.ICE)) { +- for (Direction direction1 : Direction.values()) { ++ for (Direction direction1 : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockState blockState1 = worldGenLevel.getBlockState(blockPos1.relative(direction1)); + if (blockState1.is(Blocks.BLUE_ICE)) { + worldGenLevel.setBlock(blockPos1, Blocks.BLUE_ICE.defaultBlockState(), 2); +diff --git a/net/minecraft/world/level/levelgen/feature/Feature.java b/net/minecraft/world/level/levelgen/feature/Feature.java +index d4b7275351e5b6dec6fb724c3dd455447dd1ed23..c3c021f5d1a20dab9fa14146ca6ffdb45a14421d 100644 +--- a/net/minecraft/world/level/levelgen/feature/Feature.java ++++ b/net/minecraft/world/level/levelgen/feature/Feature.java +@@ -207,7 +207,7 @@ public abstract class Feature { + public static boolean checkNeighbors(Function adjacentStateAccessor, BlockPos pos, Predicate filter) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(pos, direction); + if (filter.test(adjacentStateAccessor.apply(mutableBlockPos))) { + return true; +diff --git a/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java b/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java +index 0062e6ba05ff73ed80c5bc5d3765c8872ef081e0..e002a1ad27bc18a7fc5ac9d1ecae523a9e66886f 100644 +--- a/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/GlowstoneFeature.java +@@ -35,7 +35,7 @@ public class GlowstoneFeature extends Feature { + if (worldGenLevel.getBlockState(blockPos1).isAir()) { + int i1 = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (worldGenLevel.getBlockState(blockPos1.relative(direction)).is(Blocks.GLOWSTONE)) { + i1++; + } +diff --git a/net/minecraft/world/level/levelgen/feature/TreeFeature.java b/net/minecraft/world/level/levelgen/feature/TreeFeature.java +index ef444a4975cb7b24b6dc3b0e6b8d727265bf6011..6a191796e21b1ed2304fd89ecf79d3773878006b 100644 +--- a/net/minecraft/world/level/levelgen/feature/TreeFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/TreeFeature.java +@@ -203,7 +203,7 @@ public class TreeFeature extends Feature { + + discreteVoxelShape.fill(blockPos1.getX() - box.minX(), blockPos1.getY() - box.minY(), blockPos1.getZ() - box.minZ()); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.setWithOffset(blockPos1, direction); + if (box.isInside(mutableBlockPos)) { + int i3 = mutableBlockPos.getX() - box.minX(); +diff --git a/net/minecraft/world/level/levelgen/feature/VinesFeature.java b/net/minecraft/world/level/levelgen/feature/VinesFeature.java +index 231f91a96cb2ab2e9d246bb57704d6364979e2aa..3da252ab857f7c1e5d6cc3d979a53e660a3b98cd 100644 +--- a/net/minecraft/world/level/levelgen/feature/VinesFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/VinesFeature.java +@@ -21,7 +21,7 @@ public class VinesFeature extends Feature { + if (!worldGenLevel.isEmptyBlock(blockPos)) { + return false; + } else { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (direction != Direction.DOWN && VineBlock.isAcceptableNeighbour(worldGenLevel, blockPos.relative(direction), direction)) { + worldGenLevel.setBlock(blockPos, Blocks.VINE.defaultBlockState().setValue(VineBlock.getPropertyForFace(direction), true), 2); + return true; +diff --git a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java +index ffdcdc6ecdb2273ae5ca6db1f3dbd563fe241db3..25f4f7741832faca6a29c099056867d43cb0dcc4 100644 +--- a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java ++++ b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java +@@ -38,7 +38,7 @@ public class CreakingHeartDecorator extends TreeDecorator { + List list1 = new ArrayList<>(list); + Util.shuffle(list1, randomSource); + Optional optional = list1.stream().filter(pos -> { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (!context.checkBlock(pos.relative(direction), blockState -> blockState.is(BlockTags.LOGS))) { + return false; + } +diff --git a/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java b/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java +index f515f7c881d597072db226f60cfae2d7ebe89ea4..ed4d719c7dfbce695a8fdc59ded6aae75c273714 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/BuriedTreasurePieces.java +@@ -54,7 +54,7 @@ public class BuriedTreasurePieces { + || blockState1 == Blocks.DIORITE.defaultBlockState()) { + BlockState blockState2 = !blockState.isAir() && !this.isLiquid(blockState) ? blockState : Blocks.SAND.defaultBlockState(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + BlockPos blockPos = mutableBlockPos.relative(direction); + BlockState blockState3 = level.getBlockState(blockPos); + if (blockState3.isAir() || this.isLiquid(blockState3)) { +diff --git a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java +index ef9c2c0a665a1acf490affd9cd4496ae9d677410..27e7c1bb585f30165bd501bb8f8aab0dd147ca5b 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java +@@ -48,7 +48,7 @@ public class DesertPyramidStructure extends SinglePieceStructure { + } + } + +- ObjectArrayList list = new ObjectArrayList<>(set.stream().toList()); ++ ObjectArrayList list = new ObjectArrayList<>(set); // Leaf - paw optimization - TODO: use array + RandomSource randomSource = RandomSource.create(level.getSeed()).forkPositional().at(pieces.calculateBoundingBox().getCenter()); + Util.shuffle(list, randomSource); + int min = Math.min(set.size(), randomSource.nextInt(5, 8)); +diff --git a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +index 53d3bf1d2a1debe46e276b1db25b420be4ad9958..090e661107195589bf72d40ed86726ad2d878869 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +@@ -561,7 +561,7 @@ public class MineshaftPieces { + BlockPos.MutableBlockPos worldPos = this.getWorldPos(x, y, z); + int i = 0; + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + worldPos.move(direction); + if (box.isInside(worldPos) && level.getBlockState(worldPos).isFaceSturdy(level, worldPos, direction.getOpposite())) { + if (++i >= required) { +diff --git a/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java b/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java +index 999ac28a9e371f685554cdec2e645ebe11ddfc92..5a946ad8bec11413d4538b813f0cead40ccf6630 100644 +--- a/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java ++++ b/net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces.java +@@ -244,7 +244,7 @@ public class OceanMonumentPieces { + for (int i2 = 0; i2 < 3; i2++) { + int roomIndex = getRoomIndex(i, i2, i1); + if (roomDefinitions[roomIndex] != null) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + int i3 = i + direction.getStepX(); + int i4 = i2 + direction.getStepY(); + int i5 = i1 + direction.getStepZ(); +diff --git a/net/minecraft/world/level/material/LavaFluid.java b/net/minecraft/world/level/material/LavaFluid.java +index 5984ea9f6236a80b666fe6c33a99245389b54e8f..e681e8ccc91f8970a03a0a126a1cda61fceb9363 100644 +--- a/net/minecraft/world/level/material/LavaFluid.java ++++ b/net/minecraft/world/level/material/LavaFluid.java +@@ -138,7 +138,7 @@ public abstract class LavaFluid extends FlowingFluid { + } + + private boolean hasFlammableNeighbours(LevelReader level, BlockPos pos) { +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + if (this.isFlammable(level, pos.relative(direction))) { + return true; + } +diff --git a/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java b/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java +index 33047e446adf252a179cb0220c20a7d83f361482..9c58425f2afdbe5701910d8473fd5b344ffe31dd 100644 +--- a/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/AmphibiousNodeEvaluator.java +@@ -97,7 +97,7 @@ public class AmphibiousNodeEvaluator extends WalkNodeEvaluator { + if (pathTypeFromState == PathType.WATER) { + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + mutableBlockPos.set(x, y, z).move(direction); + PathType pathTypeFromState1 = context.getPathTypeFromState(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ()); + if (pathTypeFromState1 == PathType.BLOCKED) { +diff --git a/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +index b25d3bf3d5c575cd5c9a97a6d5ee7191467fe839..c598d91cd9fb711bc9aaa068fbb806619c0d4fe2 100644 +--- a/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java ++++ b/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +@@ -51,7 +51,7 @@ public class SwimNodeEvaluator extends NodeEvaluator { + int i = 0; + Map map = Maps.newEnumMap(Direction.class); + +- for (Direction direction : Direction.values()) { ++ for (Direction direction : Direction.VALUES) { // Leaf - paw optimization - cache direction values + Node node1 = this.findAcceptedNode(node.x + direction.getStepX(), node.y + direction.getStepY(), node.z + direction.getStepZ()); + map.put(direction, node1); + if (this.isNodeValid(node1)) { diff --git a/leaf-server/minecraft-patches/features/0273-Use-UUID-for-cure-reputation.patch b/leaf-server/minecraft-patches/features/0277-Use-UUID-for-cure-reputation.patch similarity index 90% rename from leaf-server/minecraft-patches/features/0273-Use-UUID-for-cure-reputation.patch rename to leaf-server/minecraft-patches/features/0277-Use-UUID-for-cure-reputation.patch index b0d2a608..328edd09 100644 --- a/leaf-server/minecraft-patches/features/0273-Use-UUID-for-cure-reputation.patch +++ b/leaf-server/minecraft-patches/features/0277-Use-UUID-for-cure-reputation.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use UUID for cure reputation diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java -index 70e68d56f2d781930d877f40818d9aeb377dc8af..56c44a8ad87e7c17083ba09a5381aec50b844154 100644 +index 70e68d56f2d781930d877f40818d9aeb377dc8af..2afe1c0a7670b52cf4ae13949b4f8c67449b6490 100644 --- a/net/minecraft/world/entity/monster/ZombieVillager.java +++ b/net/minecraft/world/entity/monster/ZombieVillager.java @@ -284,9 +284,10 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { @@ -16,7 +16,7 @@ index 70e68d56f2d781930d877f40818d9aeb377dc8af..56c44a8ad87e7c17083ba09a5381aec5 if (playerByUuid instanceof ServerPlayer) { CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)playerByUuid, this, villager); - level.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); -+ // level.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); // Leaf - move up ++ //level.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, villager); // Leaf - Use UUID for cure reputation - move up } } diff --git a/leaf-server/minecraft-patches/features/0278-Cache-potential-behaviors-in-Brain.patch b/leaf-server/minecraft-patches/features/0278-Cache-potential-behaviors-in-Brain.patch new file mode 100644 index 00000000..28995b66 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0278-Cache-potential-behaviors-in-Brain.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 7 Jun 2025 19:21:19 +0200 +Subject: [PATCH] Cache potential behaviors in Brain + + +diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java +index 655c3028440e62bcc01d8f1b3e808fd68484128e..7f6572d2259cd244222e5893c9e906075cc3cb92 100644 +--- a/net/minecraft/world/entity/ai/Brain.java ++++ b/net/minecraft/world/entity/ai/Brain.java +@@ -60,6 +60,7 @@ public class Brain { + private Activity defaultActivity = Activity.IDLE; + private long lastScheduleUpdate = -9999L; + ++ private ObjectArrayList> cachedPotentialBehaviors; // Leaf - Cache potential behaviors in Brain + public static Brain.Provider provider( + Collection> memoryTypes, Collection>> sensorTypes + ) { +@@ -166,6 +167,7 @@ public class Brain { + for (Brain.MemoryValue memoryValue : memoryValues) { + memoryValue.setMemoryInternal(this); + } ++ this.invalidateBehaviorCache(); // Leaf - Cache potential behaviors in Brain + } + + public DataResult serializeStart(DynamicOps ops) { +@@ -314,6 +316,7 @@ public class Brain { + this.activeActivities.clear(); + this.activeActivities.addAll(this.coreActivities); + this.activeActivities.add(activity); ++ this.invalidateBehaviorCache(); // Leaf - Cache potential behaviors in Brain + } + } + +@@ -403,11 +406,13 @@ public class Brain { + .computeIfAbsent(activity, activity1 -> new it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet<>()) // Leaf - Replace brain activity maps with optimized collection + .add((BehaviorControl)pair.getSecond()); + } ++ this.invalidateBehaviorCache(); // Leaf - Cache potential behaviors in Brain + } + + @VisibleForTesting + public void removeAllBehaviors() { + this.availableBehaviorsByPriority.clear(); ++ this.invalidateBehaviorCache(); // Leaf - Cache potential behaviors in Brain + } + + public boolean isActive(Activity activity) { +@@ -461,23 +466,44 @@ public class Brain { + } + } + +- private void startEachNonRunningBehavior(ServerLevel level, E entity) { +- long gameTime = level.getGameTime(); ++ // Leaf start - Cache potential behaviors in Brain ++ private void invalidateBehaviorCache() { ++ this.cachedPotentialBehaviors = null; ++ } ++ ++ private void rebuildBehaviorCache() { ++ this.cachedPotentialBehaviors = new ObjectArrayList<>(30); + + for (Map>> map : this.availableBehaviorsByPriority.values()) { +- for (Entry>> entry : map.entrySet()) { ++ for (Map.Entry>> entry : map.entrySet()) { + Activity activity = entry.getKey(); + if (this.activeActivities.contains(activity)) { +- for (BehaviorControl behaviorControl : entry.getValue()) { +- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { +- behaviorControl.tryStart(level, entity, gameTime); +- } ++ for (BehaviorControl task : entry.getValue()) { ++ this.cachedPotentialBehaviors.add(task); + } + } + } + } + } + ++ private ObjectArrayList> getPotentialBehaviors() { ++ if (this.cachedPotentialBehaviors == null) { ++ this.rebuildBehaviorCache(); ++ } ++ return this.cachedPotentialBehaviors; ++ } ++ ++ private void startEachNonRunningBehavior(ServerLevel level, E entity) { ++ long startTime = level.getGameTime(); ++ ++ for (BehaviorControl task : this.getPotentialBehaviors()) { ++ if (task.getStatus() == Behavior.Status.STOPPED) { ++ task.tryStart(level, entity, startTime); ++ } ++ } ++ } ++ // Leaf end - Cache potential behaviors in Brain ++ + private void tickEachRunningBehavior(ServerLevel level, E entity) { + long gameTime = level.getGameTime(); + diff --git a/leaf-server/minecraft-patches/features/0279-Use-ActivationList-on-runningBehaviors.patch b/leaf-server/minecraft-patches/features/0279-Use-ActivationList-on-runningBehaviors.patch new file mode 100644 index 00000000..564402d1 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0279-Use-ActivationList-on-runningBehaviors.patch @@ -0,0 +1,105 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 7 Jun 2025 23:22:56 +0200 +Subject: [PATCH] Use ActivationList on runningBehaviors + + +diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java +index 7f6572d2259cd244222e5893c9e906075cc3cb92..521f07a1ef8bfa708fcabf1434fdf133022ee92a 100644 +--- a/net/minecraft/world/entity/ai/Brain.java ++++ b/net/minecraft/world/entity/ai/Brain.java +@@ -61,6 +61,7 @@ public class Brain { + private long lastScheduleUpdate = -9999L; + + private ObjectArrayList> cachedPotentialBehaviors; // Leaf - Cache potential behaviors in Brain ++ private org.dreeam.leaf.util.list.ActivationList> runningBehaviors; // Leaf - Use ActivationList on runningBehaviors + public static Brain.Provider provider( + Collection> memoryTypes, Collection>> sensorTypes + ) { +@@ -273,19 +274,7 @@ public class Brain { + @Deprecated + @VisibleForDebug + public List> getRunningBehaviors() { +- List> list = new ObjectArrayList<>(); +- +- for (Map>> map : this.availableBehaviorsByPriority.values()) { +- for (Set> set : map.values()) { +- for (BehaviorControl behaviorControl : set) { +- if (behaviorControl.getStatus() == Behavior.Status.RUNNING) { +- list.add(behaviorControl); +- } +- } +- } +- } +- +- return list; ++ return this.getRunningBehaviorsList(); // Leaf - Use ActivationList on runningBehaviors + } + + public void useDefaultActivity() { +@@ -462,6 +451,7 @@ public class Brain { + long gameTime = owner.level().getGameTime(); + + for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { ++ this.getRunningBehaviorsList().setVisibility(behaviorControl, false); // Leaf - Use ActivationList on runningBehaviors + behaviorControl.doStop(level, owner, gameTime); + } + } +@@ -469,6 +459,7 @@ public class Brain { + // Leaf start - Cache potential behaviors in Brain + private void invalidateBehaviorCache() { + this.cachedPotentialBehaviors = null; ++ this.runningBehaviors = null; // Leaf - Use ActivationList on runningBehaviors + } + + private void rebuildBehaviorCache() { +@@ -486,6 +477,27 @@ public class Brain { + } + } + ++ // Leaf start - Use ActivationList on runningBehaviors ++ private void initializeRunningBehaviors() { ++ this.runningBehaviors = new org.dreeam.leaf.util.list.ActivationList<>(false); ++ ++ for (Map>> map : this.availableBehaviorsByPriority.values()) { ++ for (Set> set : map.values()) { ++ for (BehaviorControl task : set) { ++ this.runningBehaviors.addOrUpdate(task, task.getStatus() == Behavior.Status.RUNNING); ++ } ++ } ++ } ++ } ++ ++ private org.dreeam.leaf.util.list.ActivationList> getRunningBehaviorsList() { ++ if (this.runningBehaviors == null) { ++ this.initializeRunningBehaviors(); ++ } ++ return this.runningBehaviors; ++ } ++ // Leaf end - Use ActivationList on runningBehaviors ++ + private ObjectArrayList> getPotentialBehaviors() { + if (this.cachedPotentialBehaviors == null) { + this.rebuildBehaviorCache(); +@@ -499,6 +511,9 @@ public class Brain { + for (BehaviorControl task : this.getPotentialBehaviors()) { + if (task.getStatus() == Behavior.Status.STOPPED) { + task.tryStart(level, entity, startTime); ++ if (task.getStatus() == Behavior.Status.RUNNING) { ++ this.getRunningBehaviorsList().setVisibility(task, true); ++ } + } + } + } +@@ -509,6 +524,11 @@ public class Brain { + + for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { + behaviorControl.tickOrStop(level, entity, gameTime); ++ // Leaf start - Use ActivationList on runningBehaviors ++ if (behaviorControl.getStatus() != Behavior.Status.RUNNING) { ++ this.getRunningBehaviorsList().setVisibility(behaviorControl, false); ++ } ++ // Leaf end - Use ActivationList on runningBehaviors + } + } + diff --git a/leaf-server/minecraft-patches/features/0280-Paper-Fix-infinite-loop-in-RegionFile-IO.patch b/leaf-server/minecraft-patches/features/0280-Paper-Fix-infinite-loop-in-RegionFile-IO.patch new file mode 100644 index 00000000..e26612e0 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0280-Paper-Fix-infinite-loop-in-RegionFile-IO.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 9 Jun 2025 02:46:34 -0700 +Subject: [PATCH] Paper: Fix infinite loop in RegionFile IO + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/519e4224b1ba73a99c58c8fc53aab003eb6af37a + +If an exception is thrown during decompress then the read process +would be started again, which of course would eventually throw in +the decompress process. + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +index 709df35246fb328cda21679b53d44d9f96206cb3..8c2520cdea0a67eae6fa57ccb4cae660bdea5da9 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO { + LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr); + } + +- if (compoundTag == null) { ++ if (throwable == null && compoundTag == null) { // Paper - Fix infinite loop in RegionFile IO + // need to re-try from the start + this.scheduleReadIO(); + return; diff --git a/leaf-server/minecraft-patches/features/0272-do-not-log-invalid-flatten-text-component-parse.patch b/leaf-server/minecraft-patches/features/0281-do-not-log-invalid-flatten-text-component-parse.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0272-do-not-log-invalid-flatten-text-component-parse.patch rename to leaf-server/minecraft-patches/features/0281-do-not-log-invalid-flatten-text-component-parse.patch diff --git a/leaf-server/paper-patches/features/0051-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/paper-patches/features/0051-SparklyPaper-Parallel-world-ticking.patch index 3ca26db9..bb413dbd 100644 --- a/leaf-server/paper-patches/features/0051-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/paper-patches/features/0051-SparklyPaper-Parallel-world-ticking.patch @@ -250,23 +250,6 @@ index a4aa2615823d77920ff55b8aa0bcc27a54b8c3e1..e1bf7dfdb3be8f92ef2cb86d7f15a613 } + // SparklyPaper end - parallel world ticking } -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -index 548fcd9646dee0c40b6ba9b3dafb9ca157dfe324..d7af94890bfccd6ff665d920cecfa1e5be626aa4 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -@@ -40,6 +40,12 @@ class PaperEventManager { - if (listeners.length == 0) return; - // Leaf end - Skip event if no listeners - if (event.isAsynchronous() && this.server.isPrimaryThread()) { -+ // Leaf start - Parallel world ticking -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ org.dreeam.leaf.async.world.PWTEventScheduler.getScheduler().scheduleTask(event::callEvent); -+ return; -+ } -+ // Leaf end - Parallel world ticking - throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); - } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { - // Leaf start - Multithreaded tracker diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 0d2cfaef1413a343bc03829730453b03f903d5ce..bf87e5dcda7deb8f9c36a3f6b64285a97e48477f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -1591,47 +1574,3 @@ index fec0f3b72c1808c5019e95760e3fef19c45e1be0..d0991574d3c8c826e31b4448bbe6a6c7 if (event.callEvent()) { boolean result = snapshot.place(flags); return !checkSetResult || result; -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..e7c6b2ab5f2c68f3319ccd52785c8d3488a2eef7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java -@@ -19,11 +19,39 @@ class CraftAsyncTask extends CraftTask { - - @Override - public boolean isSync() { -+ // Leaf start - Parallel world ticking -+ // Return true if we should run this task synchronously when parallel world ticking is enabled and runAsyncTasksSync is true -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && -+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ return true; -+ } -+ // Leaf end - Parallel world ticking - return false; - } - - @Override - public void run() { -+ // Leaf start - Parallel world ticking -+ // If parallel world ticking is enabled and we're configured to run async tasks sync, -+ // execute the task as if it were a sync task (directly on the main thread) -+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && -+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { -+ try { -+ super.run(); -+ } catch (final Throwable t) { -+ this.getOwner().getLogger().log( -+ Level.WARNING, -+ String.format( -+ "Plugin %s generated an exception while executing task %s (forced sync mode)", -+ this.getOwner().getDescription().getFullName(), -+ this.getTaskId()), -+ t); -+ } -+ return; -+ } -+ // Leaf end - Parallel world ticking -+ -+ // Original async implementation - final Thread thread = Thread.currentThread(); - // Paper start - name threads according to running plugin - final String nameBefore = thread.getName(); diff --git a/leaf-server/paper-patches/features/0058-cache-getBiome.patch b/leaf-server/paper-patches/features/0058-cache-getBiome.patch new file mode 100644 index 00000000..9ef4307b --- /dev/null +++ b/leaf-server/paper-patches/features/0058-cache-getBiome.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Tue, 3 Jun 2025 18:51:44 +0900 +Subject: [PATCH] cache getBiome + + +diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +index 0e14f962b26823e49b192a4f97ec6c1f477ef0ff..cb6cd899fd8af069534f45a4a9e81137f941e250 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +@@ -285,6 +285,13 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel { + return this.delegate.getBiome(pos); + } + ++ // Leaf start - cache getBiome ++ @Override ++ public Holder getBiomeCached(BlockPos pos) { ++ return this.delegate.getBiomeCached(pos); ++ } ++ // Leaf end - cache getBiome ++ + @Override + public Stream getBlockStatesIfLoaded(AABB box) { + return this.delegate.getBlockStatesIfLoaded(box); diff --git a/leaf-server/paper-patches/features/0059-Paper-Optimise-CraftWorld-getLoadedChunks.patch b/leaf-server/paper-patches/features/0059-Paper-Optimise-CraftWorld-getLoadedChunks.patch new file mode 100644 index 00000000..b899e268 --- /dev/null +++ b/leaf-server/paper-patches/features/0059-Paper-Optimise-CraftWorld-getLoadedChunks.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 5 Jun 2025 08:16:25 -0700 +Subject: [PATCH] Paper: Optimise CraftWorld#getLoadedChunks + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/24cd24c8cc0df4bea0784e6919c9ba8f1852d46e + +We can use the existing full chunk map so that we do not need +to iterate over all ChunkHolders. Additionally, do not use streams +to do the iteration either. + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index bf87e5dcda7deb8f9c36a3f6b64285a97e48477f..c10390c1bdc7b8ee59dbb5ddc145315654405cbc 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -421,8 +421,23 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public Chunk[] getLoadedChunks() { +- List chunks = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.world); // Paper +- return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(CraftChunk::new).toArray(Chunk[]::new); ++ // Paper start - Optimise CraftWorld#getLoadedChunks ++ net.minecraft.server.level.ServerChunkCache serverChunkCache = this.getHandle().chunkSource; ++ ca.spottedleaf.moonrise.common.list.ReferenceList chunks = new ca.spottedleaf.moonrise.common.list.ReferenceList<>(new Chunk[serverChunkCache.fullChunks.size()]); ++ ++ for (java.util.PrimitiveIterator.OfLong iterator = serverChunkCache.fullChunks.keyIterator(); iterator.hasNext();) { ++ long chunk = iterator.nextLong(); ++ chunks.add(new CraftChunk(this.world, ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunk), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunk))); ++ } ++ ++ Chunk[] raw = chunks.getRawDataUnchecked(); ++ int size = chunks.size(); ++ if (raw.length == size) { ++ // always true when on main ++ return raw; ++ } ++ return java.util.Arrays.copyOf(raw, size); ++ // Paper end - Optimise CraftWorld#getLoadedChunks + } + + @Override diff --git a/leaf-server/paper-patches/features/0060-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch b/leaf-server/paper-patches/features/0060-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch new file mode 100644 index 00000000..bc0a0581 --- /dev/null +++ b/leaf-server/paper-patches/features/0060-Paper-Update-CraftWorld-getForceLoadedChunks-to-avoi.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Thu, 5 Jun 2025 14:55:57 -0700 +Subject: [PATCH] Paper: Update CraftWorld#getForceLoadedChunks to avoid using + getChunkAt + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper + +https://github.com/PaperMC/Paper/commit/774c40e71297c6e6d7d417639e1ce61cc79cc5ba + +Usual methods of adding force loaded chunks sync load them, so +they should be loaded already. This should avoid the more expensive +getChunkAt and more importantly allow this function to properly work +on Folia due to avoiding thread checks. + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index c10390c1bdc7b8ee59dbb5ddc145315654405cbc..4cf7763cba388b4c87e8c7f191f344a4ea70e718 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -658,7 +658,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Set chunks = new HashSet<>(); + + for (long coord : this.getHandle().getForceLoadedChunks()) { +- chunks.add(this.getChunkAt(ChunkPos.getX(coord), ChunkPos.getZ(coord))); ++ chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(coord), ChunkPos.getZ(coord))); // Paper - Update CraftWorld#getForceLoadedChunks to avoid using getChunkAt + } + + return Collections.unmodifiableCollection(chunks); diff --git a/leaf-server/paper-patches/features/0061-dump-pwt-thread.patch b/leaf-server/paper-patches/features/0061-dump-pwt-thread.patch new file mode 100644 index 00000000..2df07cf1 --- /dev/null +++ b/leaf-server/paper-patches/features/0061-dump-pwt-thread.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hayanesuru +Date: Sat, 7 Jun 2025 20:33:16 +0900 +Subject: [PATCH] dump pwt thread + + +diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java +index 79eae898539f71a73525ed29d8a752f65f5f67fa..e98279e78c9f5ddbd6dd0cdf48973453304b8e1c 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -124,6 +124,21 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre + WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.threadId(), Integer.MAX_VALUE), logger); + logger.log(Level.SEVERE, "------------------------------"); + ++ // Leaf start - dump pwt thread ++ logger.log(Level.SEVERE, "Parallel world ticking thread dump"); ++ for (Thread thread : org.apache.commons.lang3.ThreadUtils.getAllThreads()) { ++ if (MinecraftServer.getServer().serverThread == thread || thread instanceof WatchdogThread) { ++ continue; ++ } ++ if (thread instanceof ca.spottedleaf.moonrise.common.util.TickThread tickThread) { ++ if (tickThread instanceof ServerLevelTickThread tickThread1) { ++ WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(tickThread1.threadId(), Integer.MAX_VALUE), logger); ++ } ++ } ++ } ++ logger.log(Level.SEVERE, "------------------------------"); ++ // Leaf end - dump pwt thread ++ + // Paper start - Only print full dump on long timeouts + if (isLongTimeout) { + logger.log(Level.SEVERE, "Entire Thread Dump:"); diff --git a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java index 74e7cc82..be560786 100644 --- a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java +++ b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java @@ -13,21 +13,21 @@ import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.filter.AbstractFilter; import org.dreeam.leaf.config.modules.misc.SentryDSN; public class PufferfishSentryAppender extends AbstractAppender { - private static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger(PufferfishSentryAppender.class.getSimpleName()); + private static final Logger LOGGER = LogManager.getLogger(PufferfishSentryAppender.class.getSimpleName()); private static final Gson GSON = new Gson(); private final Level logLevel; public PufferfishSentryAppender(Level logLevel) { - super("PufferfishSentryAdapter", new SentryFilter(), null); + super("PufferfishSentryAdapter", new SentryFilter(), null, true, Property.EMPTY_ARRAY); this.logLevel = logLevel; } @@ -108,26 +108,5 @@ public class PufferfishSentryAppender extends AbstractAppender { } private static class SentryFilter extends AbstractFilter { - - @Override - public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, String msg, - Object... params) { - return this.filter(logger.getName()); - } - - @Override - public Result filter(Logger logger, org.apache.logging.log4j.Level level, Marker marker, Object msg, Throwable t) { - return this.filter(logger.getName()); - } - - @Override - public Result filter(LogEvent event) { - return this.filter(event == null ? null : event.getLoggerName()); - } - - private Result filter(String loggerName) { - return loggerName != null && loggerName.startsWith("gg.castaway.pufferfish.sentry") ? Result.DENY - : Result.NEUTRAL; - } } } diff --git a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java index 47558c45..75e26222 100644 --- a/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java +++ b/leaf-server/src/main/java/gg/pufferfish/pufferfish/sentry/SentryManager.java @@ -4,13 +4,13 @@ import io.sentry.Sentry; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dreeam.leaf.config.modules.misc.SentryDSN; public class SentryManager { private static final Logger LOGGER = LogManager.getLogger(SentryManager.class); private SentryManager() { - } private static boolean initialized = false; @@ -27,7 +27,7 @@ public class SentryManager { initialized = true; Sentry.init(options -> { - options.setDsn(org.dreeam.leaf.config.modules.misc.SentryDSN.sentryDsn); + options.setDsn(SentryDSN.sentryDsn); options.setMaxBreadcrumbs(100); }); diff --git a/leaf-server/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java b/leaf-server/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java index d5601de6..2b57a863 100644 --- a/leaf-server/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java +++ b/leaf-server/src/main/java/gg/pufferfish/pufferfish/util/AsyncExecutor.java @@ -1,74 +1,59 @@ package gg.pufferfish.pufferfish.util; -import com.google.common.collect.Queues; +import it.unimi.dsi.fastutil.PriorityQueue; +import it.unimi.dsi.fastutil.PriorityQueues; +import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Queue; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.NoSuchElementException; +import java.util.concurrent.locks.LockSupport; public class AsyncExecutor implements Runnable { private final Logger LOGGER = LogManager.getLogger("Leaf"); - private final Queue jobs = Queues.newArrayDeque(); - private final Lock mutex = new ReentrantLock(); - private final Condition cond = mutex.newCondition(); + private final PriorityQueue jobs = PriorityQueues.synchronize(new ObjectArrayFIFOQueue<>()); private final Thread thread; private volatile boolean killswitch = false; public AsyncExecutor(String threadName) { - this.thread = new Thread(this, threadName); + this.thread = Thread.ofPlatform() + .name(threadName) + .priority(Thread.NORM_PRIORITY - 1) + .daemon(false) + .unstarted(this); } public void start() { thread.start(); } - public void kill() { + public void join(long millis) throws InterruptedException { killswitch = true; - cond.signalAll(); + LockSupport.unpark(thread); + thread.join(millis); } public void submit(Runnable runnable) { - mutex.lock(); - try { - jobs.offer(runnable); - cond.signalAll(); - } finally { - mutex.unlock(); - } + jobs.enqueue(runnable); + LockSupport.unpark(thread); } @Override public void run() { while (!killswitch) { try { - Runnable runnable = takeRunnable(); - if (runnable != null) { - runnable.run(); + Runnable runnable; + try { + runnable = jobs.dequeue(); + } catch (NoSuchElementException e) { + LockSupport.park(); + continue; } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + runnable.run(); } catch (Exception e) { LOGGER.error("Failed to execute async job for thread {}", thread.getName(), e); } } } - - private Runnable takeRunnable() throws InterruptedException { - mutex.lock(); - try { - while (jobs.isEmpty() && !killswitch) { - cond.await(); - } - - if (jobs.isEmpty()) return null; // We've set killswitch - - return jobs.remove(); - } finally { - mutex.unlock(); - } - } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java b/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java index 0ffa8fb1..06be488a 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/LeafBootstrap.java @@ -4,10 +4,11 @@ import io.papermc.paper.PaperBootstrap; import joptsimple.OptionSet; public class LeafBootstrap { + public static final boolean enableFMA = Boolean.parseBoolean(System.getProperty("Leaf.enableFMA", "false")); // Leaf - FMA feature public static void boot(final OptionSet options) { - runPreBootTasks(); + //runPreBootTasks(); PaperBootstrap.boot(options); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java new file mode 100644 index 00000000..b3d1f658 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/ShutdownExecutors.java @@ -0,0 +1,60 @@ +package org.dreeam.leaf.async; + +import net.minecraft.server.MinecraftServer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dreeam.leaf.async.ai.AsyncGoalThread; +import org.dreeam.leaf.async.path.AsyncPathProcessor; +import org.dreeam.leaf.async.tracker.MultithreadedTracker; + +import java.util.concurrent.TimeUnit; + +public class ShutdownExecutors { + public static final Logger LOGGER = LogManager.getLogger("Leaf"); + + public static void shutdown(MinecraftServer server) { + if (server.mobSpawnExecutor != null) { + LOGGER.info("Waiting for mob spawning thread to shutdown..."); + try { + server.mobSpawnExecutor.join(3000L); + } catch (InterruptedException ignored) { + } + } + + if (AsyncPlayerDataSaving.IO_POOL != null) { + LOGGER.info("Waiting for player I/O executor to shutdown..."); + AsyncPlayerDataSaving.IO_POOL.shutdown(); + try { + AsyncPlayerDataSaving.IO_POOL.awaitTermination(60L, TimeUnit.SECONDS); + } catch (InterruptedException ignored) { + } + } + + if (server.asyncGoalThread != null) { + LOGGER.info("Waiting for mob target finding thread to shutdown..."); + AsyncGoalThread.RUNNING = false; + try { + server.asyncGoalThread.join(3000L); + } catch (InterruptedException ignored) { + } + } + + if (MultithreadedTracker.TRACKER_EXECUTOR != null) { + LOGGER.info("Waiting for mob tracker executor to shutdown..."); + MultithreadedTracker.TRACKER_EXECUTOR.shutdown(); + try { + MultithreadedTracker.TRACKER_EXECUTOR.awaitTermination(10L, TimeUnit.SECONDS); + } catch (InterruptedException ignored) { + } + } + + if (AsyncPathProcessor.pathProcessingExecutor != null) { + LOGGER.info("Waiting for mob pathfinding executor to shutdown..."); + AsyncPathProcessor.pathProcessingExecutor.shutdown(); + try { + AsyncPathProcessor.pathProcessingExecutor.awaitTermination(10L, TimeUnit.SECONDS); + } catch (InterruptedException ignored) { + } + } + } +} 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 06bd5e48..e989adbf 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 @@ -9,6 +9,7 @@ import java.util.concurrent.locks.LockSupport; public class AsyncGoalThread extends Thread { + public static volatile boolean RUNNING = true; public AsyncGoalThread(final MinecraftServer server) { super(() -> run(server), "Leaf Async Goal Thread"); this.setDaemon(false); @@ -18,7 +19,7 @@ public class AsyncGoalThread extends Thread { } private static void run(MinecraftServer server) { - while (server.isRunning()) { + while (RUNNING) { boolean retry = false; for (ServerLevel level : server.getAllLevels()) { var exec = level.asyncGoalExecutor; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java index 7ebcec15..3fac5839 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/path/AsyncPathProcessor.java @@ -28,7 +28,7 @@ public class AsyncPathProcessor { private static final String THREAD_PREFIX = "Leaf Async Pathfinding"; private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX); private static long lastWarnMillis = System.currentTimeMillis(); - private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor( + public static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor( 1, AsyncPathfinding.asyncPathfindingMaxThreads, AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS, diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java index e55dd632..7e37d3e1 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java @@ -30,7 +30,7 @@ public class MultithreadedTracker { private static final String THREAD_PREFIX = "Leaf Async Tracker"; private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX); private static long lastWarnMillis = System.currentTimeMillis(); - private static ThreadPoolExecutor TRACKER_EXECUTOR = null; + public static ThreadPoolExecutor TRACKER_EXECUTOR = null; private MultithreadedTracker() { } 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 c2ebeb42..ec1a8a7b 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/AsyncTargetFinding.java @@ -3,6 +3,7 @@ package org.dreeam.leaf.config.modules.async; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.LeafConfig; public class AsyncTargetFinding extends ConfigModules { @@ -33,6 +34,11 @@ public class AsyncTargetFinding extends ConfigModules { asyncTargetFindingInitialized = true; 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."); + 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); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java index 304e234f..1d89ae82 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/async/SparklyPaperParallelWorldTicking.java @@ -17,7 +17,7 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules { public static boolean logContainerCreationStacktraces = false; public static boolean disableHardThrow = false; @Deprecated - public static boolean runAsyncTasksSync = false; + public static Boolean runAsyncTasksSync; // STRICT, BUFFERED, DISABLED public static String asyncUnsafeReadHandling = "BUFFERED"; @@ -52,15 +52,14 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules { asyncUnsafeReadHandling = "DISABLED"; } - runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", false); // Default to false now - if (runAsyncTasksSync) { - LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated. Use 'async-unsafe-read-handling: STRICT' for similar safety checks or 'BUFFERED' for buffered reads.", getBasePath()); + // Transfer old config + runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync"); + if (runAsyncTasksSync != null && runAsyncTasksSync) { + LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated, removed automatically. Use 'async-unsafe-read-handling: BUFFERED' for buffered reads instead.", getBasePath()); } if (enabled) { LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads); } - - runAsyncTasksSync = enabled && runAsyncTasksSync; // Auto-disable if main feature is off } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java index b0610c61..0e794856 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/Knockback.java @@ -15,6 +15,7 @@ public class Knockback extends ConfigModules { public static boolean canPlayerKnockbackZombie = true; @Experimental public static boolean flushKnockback = false; + public static boolean oldBlastProtectionKnockbackBehavior = false; @Override public void onLoaded() { @@ -34,5 +35,6 @@ public class Knockback extends ConfigModules { "使玩家可以击退僵尸." )); flushKnockback = config.getBoolean(getBasePath() + ".flush-location-while-knockback-player", flushKnockback); + oldBlastProtectionKnockbackBehavior = config.getBoolean(getBasePath() + ".old-blast-protection-explosion-knockback", oldBlastProtectionKnockbackBehavior); } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java deleted file mode 100644 index b9e6dfca..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SmoothTeleport.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.dreeam.leaf.config.modules.gameplay; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; -import org.dreeam.leaf.config.annotations.Experimental; - -public class SmoothTeleport extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".smooth-teleport"; - } - - @Experimental - public static boolean enabled = false; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased(""" - **Experimental feature** - Whether to make a "smooth teleport" when players changing dimension. - This requires original world and target world have same logical height to work.""", - """ - **实验性功能** - 是否在玩家切换世界时尝试使用 "平滑传送". - 此项要求源世界和目标世界逻辑高度相同才会生效.""" - )); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java index 1aea4693..1e40980c 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/network/ProtocolSupport.java @@ -29,7 +29,6 @@ public class ProtocolSupport extends ConfigModules { public static boolean doABarrelRollForceEnabled = false; public static boolean doABarrelRollForceInstalled = false; public static int doABarrelRollInstalledTimeout = 40; - public static DoABarrelRollPackets.KineticDamage doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.VANILLA; @Override public void onLoaded() { @@ -53,7 +52,6 @@ public class ProtocolSupport extends ConfigModules { doABarrelRollForceEnabled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-enabled", doABarrelRollForceEnabled); doABarrelRollForceInstalled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-installed", doABarrelRollForceInstalled); doABarrelRollInstalledTimeout = config.getInt(getBasePath() + ".do-a-barrel-roll-installed-timeout", 0); - doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.valueOf(config.getString(getBasePath() + ".do-a-barrel-roll-kinetic-damage", doABarrelRollKineticDamage.name())); if (doABarrelRollInstalledTimeout <= 0) { doABarrelRollInstalledTimeout = 40; } @@ -63,7 +61,7 @@ public class ProtocolSupport extends ConfigModules { doABarrelRollForceEnabled, doABarrelRollForceInstalled, doABarrelRollInstalledTimeout, - doABarrelRollKineticDamage + DoABarrelRollPackets.KineticDamage.VANILLA ); } else { DoABarrelRollProtocol.deinit(); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java deleted file mode 100644 index f1ae5bf9..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/BrainRunningBehaviorCacheUpdate.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class BrainRunningBehaviorCacheUpdate extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static int interval = 5; - - @Override - public void onLoaded() { - interval = config.getInt(getBasePath() + ".entity-running-behavior-cache-update-interval", interval, - config.pickStringRegionBased( - "How often entity update current brain running behavior list.", - "生物更新现有 Brain Behavior 列表缓存的间隔.")); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java deleted file mode 100644 index e1ddf5eb..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimiseBlockEntities.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class OptimiseBlockEntities extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static boolean enabled = true; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath() + ".optimise-block-entities", enabled); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java new file mode 100644 index 00000000..27d2a9d3 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBiome.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizeBiome extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".cache-biome"; + } + + public static boolean enabled = false; + public static boolean mobSpawn = false; + public static boolean advancement = false; + + @Override + public void onLoaded() { + enabled = config().getBoolean(getBasePath() + ".enabled", enabled); + mobSpawn = config.getBoolean(getBasePath() + ".mob-spawning", false); + advancement = config.getBoolean(getBasePath() + ".advancements", false); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java new file mode 100644 index 00000000..4e76dea4 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeBlockEntities.java @@ -0,0 +1,24 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class OptimizeBlockEntities extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName(); + } + + public static boolean enabled = true; + + @Override + public void onLoaded() { + // Transfer old config + Boolean optimiseBlockEntities = config.getBoolean(getBasePath() + ".optimise-block-entities"); + if (optimiseBlockEntities != null && optimiseBlockEntities) { + enabled = true; + } + + enabled = config.getBoolean(getBasePath() + ".optimize-block-entities", enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java index 3f407850..f8016d34 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizePlayerMovementProcessing.java @@ -17,6 +17,5 @@ public class OptimizePlayerMovementProcessing extends ConfigModules { Whether to optimize player movement processing by skipping unnecessary edge checks and avoiding redundant view distance updates.""", """ 是否优化玩家移动处理,跳过不必要的边缘检查并避免冗余的视距更新。""")); - } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java new file mode 100644 index 00000000..50a5742e --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/PreloadNaturalMobSpawning.java @@ -0,0 +1,17 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class PreloadNaturalMobSpawning extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".preload-mob-spawning-position"; + } + + public static boolean enabled = false; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java index e89a0ad2..77fb1a41 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ReduceChunkSourceUpdates.java @@ -15,7 +15,7 @@ public class ReduceChunkSourceUpdates extends ConfigModules { public void onLoaded() { enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config.pickStringRegionBased( - "Reduces chunk source updates on inter-chunk player moves. (Recommended to enable)", + "Reduces chunk source updates on inter-chunk player moves.", "减少玩家跨区块移动时的区块源更新。" ) ); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java new file mode 100644 index 00000000..8dbcff11 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleNaturalMobSpawning.java @@ -0,0 +1,31 @@ +package org.dreeam.leaf.config.modules.opt; + +import net.minecraft.world.entity.MobCategory; +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class ThrottleNaturalMobSpawning extends ConfigModules { + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".throttle-mob-spawning"; + } + + public static boolean enabled = false; + public static long[] failedAttempts; + public static int[] spawnChance; + + @Override + public void onLoaded() { + enabled = config.getBoolean(getBasePath() + ".enabled", enabled); + MobCategory[] categories = MobCategory.values(); + failedAttempts = new long[categories.length]; + spawnChance = new int[categories.length]; + for (int i = 0; i < categories.length; i++) { + String category = getBasePath() + "." + categories[i].getSerializedName(); + long attempts = config.getLong(category + ".min-failed", 8); + double chance = config.getDouble(category + ".spawn-chance", 25.0); + + failedAttempts[i] = Math.max(-1, attempts); + spawnChance[i] = Math.clamp(0, (int) Math.round(chance * 10.24), 1024); + } + } +} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java deleted file mode 100644 index 22fb0741..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/VT4DownloadPool.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.dreeam.leaf.config.modules.opt; - -import org.dreeam.leaf.config.ConfigModules; -import org.dreeam.leaf.config.EnumConfigCategory; - -public class VT4DownloadPool extends ConfigModules { - - public String getBasePath() { - return EnumConfigCategory.PERF.getBaseKeyName(); - } - - public static boolean enabled = true; - - @Override - public void onLoaded() { - enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-download-pool", enabled, - config.pickStringRegionBased( - "Use the new Virtual Thread introduced in JDK 21 for download worker pool.", - "是否为下载工作线程池使用虚拟线程(如果可用)。")); - } -} diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java index af88f866..8859fe24 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/DoABarrelRollProtocol.java @@ -1,14 +1,7 @@ package org.dreeam.leaf.protocol; import com.google.common.collect.ImmutableList; -import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; -import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps; -import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2FloatMap; -import it.unimi.dsi.fastutil.objects.Reference2FloatMaps; -import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; @@ -25,7 +18,6 @@ import org.dreeam.leaf.protocol.DoABarrelRollPackets.KineticDamage; import org.dreeam.leaf.protocol.DoABarrelRollPackets.ModConfigServer; import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncC2SPacket; import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncS2CPacket; -import org.jetbrains.annotations.NotNull; import org.bukkit.event.player.PlayerKickEvent; import java.util.List; @@ -54,8 +46,8 @@ public class DoABarrelRollProtocol implements Protocol { private ModConfigServer config = DEFAULT; private boolean configUpdated = false; - private final Reference2ReferenceMap syncStates = new Reference2ReferenceOpenHashMap<>(); - private final Reference2ReferenceMap scheduledKicks = new Reference2ReferenceOpenHashMap<>(); + private final Reference2ReferenceMap syncStates = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); + private final Reference2ReferenceMap scheduledKicks = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); public final Reference2BooleanMap isRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); public final Reference2FloatMap rollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>()); public final Reference2BooleanMap lastIsRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>()); @@ -99,14 +91,14 @@ public class DoABarrelRollProtocol implements Protocol { } @Override - public void handle(ServerPlayer player, @NotNull LeafCustomPayload payload) { + public void handle(ServerPlayer player, LeafCustomPayload payload) { switch (payload) { case ConfigUpdateC2SPacket ignored -> player.connection.send(Protocols.createPacket(new ConfigUpdateAckS2CPacket(PROTOCOL_VERSION, false))); case ConfigResponseC2SPacket configResponseC2SPacket -> { var reply = clientReplied(player.connection, configResponseC2SPacket); if (reply == HandshakeState.RESEND) { - sendHandshake(player); + sendHandshake(player.connection); } } case RollSyncC2SPacket rollSyncC2SPacket -> { @@ -138,41 +130,39 @@ public class DoABarrelRollProtocol implements Protocol { } @Override - public void tickTracker(ServerPlayer player) { - if (!isRollingMap.containsKey(player.connection)) { + public void tickPlayer(ServerPlayer player) { + ServerGamePacketListenerImpl connection = player.connection; + if (getHandshakeState(connection).state == HandshakeState.NOT_SENT) { + sendHandshake(connection); + } + if (!isRollingMap.containsKey(connection)) { return; } + if (!isRollingMap.getBoolean(connection)) { + rollMap.put(connection, 0.0F); + } - var isRolling = isRollingMap.getBoolean(player.connection); - var roll = rollMap.getFloat(player.connection); - var lastIsRolling = lastIsRollingMap.getBoolean(player.connection); - var lastRoll = lastRollMap.getFloat(player.connection); + boolean isRolling = isRollingMap.getBoolean(connection); + float roll = rollMap.getFloat(connection); + boolean lastIsRolling = lastIsRollingMap.getBoolean(connection); + float lastRoll = lastRollMap.getFloat(connection); if (isRolling == lastIsRolling && roll == lastRoll) { return; } var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll); var packet = Protocols.createPacket(payload); - for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy()) { + var tracked = player.moonrise$getTrackedEntity(); + if (tracked == null) { + return; + } + for (ServerPlayerConnection seenBy : tracked.seenBy()) { if (seenBy instanceof ServerGamePacketListenerImpl conn && getHandshakeState(conn).state == HandshakeState.ACCEPTED) { seenBy.send(packet); } } - lastIsRollingMap.put(player.connection, isRolling); - lastRollMap.put(player.connection, roll); - } - - @Override - public void tickPlayer(ServerPlayer player) { - if (getHandshakeState(player.connection).state == HandshakeState.NOT_SENT) { - sendHandshake(player); - } - if (!isRollingMap.containsKey(player.connection)) { - return; - } - if (!isRollingMap.getBoolean(player.connection)) { - rollMap.put(player.connection, 0.0F); - } + lastIsRollingMap.put(connection, isRolling); + lastRollMap.put(connection, roll); } @Override @@ -190,7 +180,7 @@ public class DoABarrelRollProtocol implements Protocol { if (configUpdated) { configUpdated = false; for (ServerPlayer player : server.getPlayerList().players) { - sendHandshake(player); + sendHandshake(player.connection); } } } @@ -199,9 +189,9 @@ public class DoABarrelRollProtocol implements Protocol { return config.forceInstalled() ? OptionalInt.of(config.installedTimeout()) : OptionalInt.empty(); } - private void sendHandshake(ServerPlayer player) { - player.connection.send(Protocols.createPacket(initiateConfigSync(player.connection))); - configSentToClient(player.connection); + private void sendHandshake(ServerGamePacketListenerImpl connection) { + connection.send(Protocols.createPacket(initiateConfigSync(connection))); + configSentToClient(connection); } private void configSentToClient(ServerGamePacketListenerImpl handler) { @@ -255,7 +245,7 @@ public class DoABarrelRollProtocol implements Protocol { return info.state; } - private boolean isLimited(ServerGamePacketListenerImpl net) { + private boolean isLimited(ServerGamePacketListenerImpl ignore) { return true; // return net.getPlayer().getBukkitEntity().hasPermission(DoABarrelRoll.MODID + ".configure"); } @@ -266,19 +256,19 @@ public class DoABarrelRollProtocol implements Protocol { private ConfigSyncS2CPacket initiateConfigSync(ServerGamePacketListenerImpl handler) { var isLimited = isLimited(handler); - getHandshakeState(handler).isLimited = isLimited; + // getHandshakeState(handler).isLimited = isLimited; return new ConfigSyncS2CPacket(PROTOCOL_VERSION, config, isLimited, isLimited ? DEFAULT : config); } private static class ClientInfo { private HandshakeState state; private int protocolVersion; - private boolean isLimited; + // private boolean isLimited; - private ClientInfo(HandshakeState state, int protocolVersion, boolean isLimited) { + private ClientInfo(HandshakeState state, int protocolVersion, boolean ignore) { this.state = state; this.protocolVersion = protocolVersion; - this.isLimited = isLimited; + // this.isLimited = isLimited; } } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java index 61c5c529..f1b2bca7 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocol.java @@ -3,7 +3,6 @@ package org.dreeam.leaf.protocol; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; -import org.jetbrains.annotations.NotNull; import java.util.List; @@ -19,9 +18,7 @@ interface Protocol { void tickPlayer(ServerPlayer player); - void tickTracker(ServerPlayer player); - void disconnected(ServerPlayer conn); - void handle(ServerPlayer player, @NotNull LeafCustomPayload payload); + void handle(ServerPlayer player, LeafCustomPayload payload); } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java index 000b6f93..89701f15 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/protocol/Protocols.java @@ -61,12 +61,6 @@ public class Protocols { } } - public static void tickTracker(ServerPlayer player) { - for (Protocol protocol : PROTOCOLS) { - protocol.tickTracker(player); - } - } - public static void disconnected(ServerPlayer conn) { for (Protocol protocol : PROTOCOLS) { protocol.disconnected(conn); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java b/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java new file mode 100644 index 00000000..21fad340 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/list/ActivationList.java @@ -0,0 +1,215 @@ +package org.dreeam.leaf.util.list; + +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.AbstractList; +import java.util.BitSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.function.Consumer; + +/** + * A specialized list that allows for efficient hiding and showing of elements + * without physically removing them from the backing store. + *

+ * Iteration only processes "visible" elements, and visibility can be toggled in O(1) time. + * This is useful for managing lists of tasks or objects where a large set exists, + * but only a small subset is active at any given time. + * + * @param The type of elements in this list. + */ +public class ActivationList extends AbstractList { + + private final ObjectArrayList elements; + private final BitSet visibilityMask; + private final Object2IntOpenHashMap elementToIndexMap; + private final boolean isVisibleByDefault; + private int removedSlotCount; + + /** + * Constructs a new, empty MaskedList. + * + * @param isVisibleByDefault The default visibility for elements added to this list. + */ + public ActivationList(boolean isVisibleByDefault) { + this.elements = new ObjectArrayList<>(); + this.visibilityMask = new BitSet(); + this.elementToIndexMap = new Object2IntOpenHashMap<>(); + this.elementToIndexMap.defaultReturnValue(-1); + this.isVisibleByDefault = isVisibleByDefault; + } + + /** + * Constructs a new, empty MaskedList with default visibility set to true. + */ + public ActivationList() { + this(true); + } + + /** + * Adds an element to the list or, if it already exists, updates its visibility. + * + * @param element The element to add or update. + * @param visible The desired visibility of the element. + */ + public void addOrUpdate(E element, boolean visible) { + int index = this.elementToIndexMap.getInt(element); + if (index == -1) { + index = this.elements.size(); + this.elements.add(element); + this.elementToIndexMap.put(element, index); + } + this.visibilityMask.set(index, visible); + } + + /** + * Sets the visibility of an existing element. + * + * @param element The element whose visibility to change. + * @param visible True to make the element visible, false to hide it. + */ + public void setVisibility(E element, boolean visible) { + int index = this.elementToIndexMap.getInt(element); + if (index != -1) { + this.visibilityMask.set(index, visible); + } + } + + @Override + public boolean add(E element) { + if (this.elementToIndexMap.containsKey(element)) { + throw new IllegalArgumentException("MaskedList cannot contain duplicate elements: " + element); + } + this.addOrUpdate(element, this.isVisibleByDefault); + return true; + } + + @Override + public boolean remove(Object o) { + int index = this.elementToIndexMap.removeInt(o); + if (index == -1) { + return false; + } + + this.visibilityMask.clear(index); + this.elements.set(index, null); + this.removedSlotCount++; + + if (this.removedSlotCount > 0 && this.removedSlotCount * 2 >= this.elements.size()) { + compact(); + } + return true; + } + + /** + * Rebuilds the internal list (wow) + */ + private void compact() { + int writeIndex = 0; + for (int readIndex = 0; readIndex < this.elements.size(); readIndex++) { + E element = this.elements.get(readIndex); + + if (element != null) { + if (readIndex != writeIndex) { + this.elements.set(writeIndex, element); + this.elementToIndexMap.put(element, writeIndex); + this.visibilityMask.set(writeIndex, this.visibilityMask.get(readIndex)); + } + writeIndex++; + } + } + + int oldSize = this.elements.size(); + if (writeIndex < oldSize) { + this.elements.removeElements(writeIndex, oldSize); + this.visibilityMask.clear(writeIndex, oldSize); + } + + this.removedSlotCount = 0; + } + + @Override + public int size() { + return this.visibilityMask.cardinality(); + } + + @Override + public E get(int index) { + if (index < 0 || index >= this.size()) { + throw new IndexOutOfBoundsException("Index: " + index + ", Visible Size: " + this.size()); + } + + int setBitIndex = -1; + for (int i = 0; i <= index; i++) { + setBitIndex = this.visibilityMask.nextSetBit(setBitIndex + 1); + } + return this.elements.get(setBitIndex); + } + + @Override + public Iterator iterator() { + return new MaskedIterator(); + } + + private class MaskedIterator implements Iterator { + private int nextVisibleIndex; + + MaskedIterator() { + this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(0); + } + + @Override + public boolean hasNext() { + return this.nextVisibleIndex != -1; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + E element = ActivationList.this.elements.get(this.nextVisibleIndex); + this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(this.nextVisibleIndex + 1); + return element; + } + } + + @Override + public Spliterator spliterator() { + return new MaskedSpliterator(); + } + + private class MaskedSpliterator implements Spliterator { + private int currentIndex; + + MaskedSpliterator() { + this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(0); + } + + @Override + public boolean tryAdvance(Consumer action) { + if (this.currentIndex != -1) { + action.accept(ActivationList.this.elements.get(this.currentIndex)); + this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(this.currentIndex + 1); + return true; + } + return false; + } + + @Override + public Spliterator trySplit() { + return null; // This spliterator does not support splitting. + } + + @Override + public long estimateSize() { + return ActivationList.this.size(); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + } + } +} diff --git a/public/readme/README_CN.md b/public/readme/README_CN.md new file mode 100644 index 00000000..4b1be0f8 --- /dev/null +++ b/public/readme/README_CN.md @@ -0,0 +1,141 @@ +Leaf +

+ +[![下载](https://img.shields.io/badge/releases-blue?label=%e4%b8%8b%e8%bd%bd&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/zh/download)⠀ +[![Github Actions 构建](https://img.shields.io/github/actions/workflow/status/Winds-Studio/Leaf/build-1214.yml?label=%e6%9e%84%e5%bb%ba&style=for-the-badge&colorA=19201a&colorB=298046)](https://github.com/Winds-Studio/Leaf/actions)⠀ +![QQ](https://img.shields.io/badge/619278377-blue?label=QQ%e7%be%a4&style=for-the-badge&colorA=19201a&colorB=298046) +[![文档](https://img.shields.io/badge/leafmc.one/zh/docs-blue?label=%e6%96%87%e6%a1%a3&style=for-the-badge&colorA=19201a&colorB=298046)](https://www.leafmc.one/zh/docs) + +**Leaf** 是一个基于 [Paper](https://papermc.io/) 的分支,专为高自定义和高性能而设计,基于 [Gale](https://github.com/Dreeam-qwq/Gale) 之上,并融合了其他核心的优化和修复。 +
+ +> [!WARNING] +> Leaf 是一个面向性能的分支。在迁移到 Leaf 之前,请务必**提前备份**。欢迎任何人贡献优化或报告问题来帮助我们改进。 + +[English](../../README.md) | **中文** + +## 🍃 特点 +- **基于 [Gale](https://github.com/Dreeam-qwq/Gale)**,以获得更好的性能 +- **异步**寻路、生物生成和实体追踪 +- **大量优化**融合自 [其他核心](#-致谢) 和我们自己的的补丁 +- **完全兼容** Spigot 和 Paper 插件 +- **最新依赖**,保持所有依赖项为最新版本 +- **允许用户名使用所有字符**,包括中文和其他字符 +- **修复**一些 Minecraft 的 bug +- **模组协议**支持 +- **更多自定义配置项**,源自 [Purpur](https://github.com/PurpurMC/Purpur) 的特性 +- **线性区域文件格式**,节省磁盘空间 +- **运维友好**,集成 [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) 的 [Sentry](https://sentry.io/welcome/),轻松详细追踪服务器的所有报错 +- 以及更多... + +## 📈 bStats 统计 +[![bStats Graph Data](https://bstats.org/signatures/server-implementation/Leaf.svg)](https://bstats.org/plugin/server-implementation/Leaf) + +## 📫 联系方式 +- Discord: [`https://discord.com/invite/gfgAwdSEuM`](https://discord.com/invite/gfgAwdSEuM) +- QQ社区群: `619278377` + +## 📫 赞助 +如果您喜欢我们的工作,欢迎通过我们的 [Open Collective](https://opencollective.com/Winds-Studio) 或 [Dreeam 的爱发电](https://afdian.com/a/Dreeam) 进行赞助 :) + +## 📥 下载 +从我们的 [官网](https://www.leafmc.one/zh/download) 下载 Leaf,或在 [GitHub Action](https://github.com/Winds-Studio/Leaf/actions) 获取最新构建版本 + +**请注意需要 Java 21 以上。** + +## 📄 文档 +关于如何使用/配置 Leaf 的文档:[www.leafmc.one/zh/docs](https://www.leafmc.one/zh/docs) + +## 📦 构建 +构建用于分发的 Paperclip JAR: +```bash +./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar +``` + +## 🧪 API + +### Maven +```xml + + leafmc + https://maven.nostal.ink/repository/maven-snapshots/ + +``` +```xml + + cn.dreeam.leaf + leaf-api + 1.21.4-R0.1-SNAPSHOT + provided + +``` +### Gradle +```kotlin +repositories { + maven { + url = uri("https://maven.nostal.ink/repository/maven-snapshots/") + } +} + +dependencies { + compileOnly("cn.dreeam.leaf:leaf-api:1.21.4-R0.1-SNAPSHOT") +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} +``` + +## ⚖️ 许可证 +Paperweight 文件基于 [MIT](licenses/MIT.txt) 许可证。 +补丁基于 [MIT](licenses/MIT.txt) 许可证,除非在补丁顶部注释中另有说明。 +二进制文件基于 [GPL-3.0](licenses/GPL-3.0.txt) 许可证。 + +另请参阅 [PaperMC/Paper](https://github.com/PaperMC/Paper) 和 [PaperMC/paperweight](https://github.com/PaperMC/paperweight) 了解本项目使用的一些材料的许可证。 + +## 📜 致谢 +感谢以下项目。Leaf 包含了一些取自这些项目的补丁。
+如果没有这些优秀的项目,Leaf 就不会变得如此出色。 + +- [Gale](https://github.com/Dreeam-qwq/Gale) ([原始仓库](https://github.com/GaleMC/Gale)) +- [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) +- [Purpur](https://github.com/PurpurMC/Purpur) +-
+ 🍴 展开查看 Leaf 采用补丁的核心 +

+ • KeYi (R.I.P.) + (备份仓库)
+ • Mirai
+ • Petal
+ • Carpet Fixes
+ • Akarin
+ • Slice
+ • Parchment
+ • Leaves
+ • Kaiiju
+ • Plazma
+ • SparklyPaper
+ • Polpot
+ • Matter
+ • Luminol
+ • Nitori
+ • Moonrise (在 1.21.1 期间)
+ • Sakura
+

+
+ +## 🔥 特别感谢 +Jianke Cloud Host +剑客云 | cloud of swordsman + +如果你想找一个低价高性能、低延迟的云服务商,剑客云是个不错的选择!你可以在 [这里](https://cloud.swordsman.com.cn/?i8ab42c) 注册。 + +If you want to find a cheaper, high performance, stable, lower latency host, then cloud of swordsman is a good choice! Registers and purchases in [here](https://cloud.swordsman.com.cn/?i8ab42c). + +--- +![YourKit](https://www.yourkit.com/images/yklogo.png) + +YourKit 通过创新和智能的工具支持开源项目,用于监控和分析 Java 和 .NET 应用程序。 +YourKit 是 [YourKit Java Profiler](https://www.yourkit.com/java/profiler/)、 +[YourKit .NET Profiler](https://www.yourkit.com/dotnet-profiler/) 和 +[YourKit YouMonitor](https://www.yourkit.com/youmonitor/) 的创造者。 \ No newline at end of file diff --git a/todos.md b/todos.md index 4f35294c..d721eef5 100644 --- a/todos.md +++ b/todos.md @@ -8,9 +8,8 @@ # 1.21.5 - [ ] Check beacon issues fix -- [ ] Update README.md - [ ] Remove stream in Inventory and check new changes - [ ] Check Purpur's Projectile offset config, in BowItem shoot - [ ] Remove Gale's attribute patch - [ ] Check SparklyPaper's mapitem update skip -- [ ] Update from Leaf 1.21.4 (curr commit: `12711630d4ca8788366f8abd47275c4c262ff13f`) +- [ ] Update from Leaf 1.21.4 (curr commit: `ccab3256c296857eeacd4d0c23f2e1b8aae3b36c`)