diff --git a/.gitignore b/.gitignore
index 62b6c22a..44a9d442 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,7 @@ build
# Leaf
build-data/dev-imports.txt
-patches/todo
+leaf-archived-patches/todo
run
leaf-api/build.gradle.kts
diff --git a/README.md b/README.md
index 04813f4a..08609ec3 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,32 @@
-[](https://github.com/Winds-Studio/Leaf/releases)⠀
+[](https://www.leafmc.one/download)⠀
[](https://github.com/Winds-Studio/Leaf/actions)⠀
[](https://discord.gg/gfgAwdSEuM)
-[](https://www.leafmc.one/docs/)
+[](https://www.leafmc.one/docs)
**Leaf** is a [Paper](https://papermc.io/) fork designed to be customizable and high-performance, built on top of [Gale](https://github.com/Dreeam-qwq/Gale) with optimizations and fixes from other forks.
-> [!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 [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance
- - **Async** pathfinding, mob spawning and entity tracker
- - **Various optimizations** blending from [other forks](https://github.com/Winds-Studio/Leaf#-credits)
- - **Fully compatible** with Bukkit, 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
- - **Configurable UseItem distance** for anarchy servers
- - **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 easily track all errors coming from your server in extreme detail
- - And more...
+- **Based on [Gale](https://github.com/Dreeam-qwq/Gale)** for better performance
+- **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 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
[](https://bstats.org/plugin/server-implementation/Leaf)
@@ -38,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:
@@ -90,7 +91,7 @@ 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 licenses of some materials 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.
@@ -129,7 +130,7 @@ cloud of swordsman | 剑客云
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) 注册。
---

diff --git a/build-data/leaf.at b/build-data/leaf.at
index 21a98f49..f5530961 100644
--- a/build-data/leaf.at
+++ b/build-data/leaf.at
@@ -4,6 +4,7 @@ protected net.minecraft.world.entity.Entity dimensions
protected net.minecraft.world.entity.ai.goal.RemoveBlockGoal blockToRemove
protected net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal getTargetConditions()Lnet/minecraft/world/entity/ai/targeting/TargetingConditions;
protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape
+public net.minecraft.core.Direction VALUES
public net.minecraft.server.level.ServerChunkCache fullChunks
public net.minecraft.server.level.ServerEntity sendDirtyEntityData()V
public net.minecraft.util.Mth SIN
diff --git a/gradle.properties b/gradle.properties
index f39575df..040eb13b 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/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index a4b76b95..1b33c55b 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 37f853b1..ff23a68d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index f3b75f3b..23d15a93 100755
--- a/gradlew
+++ b/gradlew
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -205,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
diff --git a/gradlew.bat b/gradlew.bat
index 9d21a218..db3a6ac2 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+set CLASSPATH=
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch
index c584cf3f..5c1e1b4b 100644
--- a/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch
+++ b/leaf-server/minecraft-patches/features/0188-Paw-optimization.patch
@@ -7,6 +7,7 @@ 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
@@ -234,6 +235,248 @@ index 80baa2dff5c1034a72271fc727fdb2acc1b69488..9f581d5bdc3f658694bbd8c80abbce4e
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 e804ca8d6c9c9b8d9f982a970cc3edddf5c03aa1..0a4ca9f0dba64c5539b99af5d95292159f51b6ba 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 45df9f008b74dd0d6d790c91102e9afe1da45633..baee320ad6f1918cc9009db9f65c28e33a544e19 100644
+--- a/net/minecraft/world/level/block/BaseFireBlock.java
++++ b/net/minecraft/world/level/block/BaseFireBlock.java
+@@ -204,7 +204,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 7340c664fdcf991a2549c8f07f6ab093bbe6e4e8..90f1a971888a76ab3f8522d86a9f9f240ef76a53 100644
+--- a/net/minecraft/world/level/block/FireBlock.java
++++ b/net/minecraft/world/level/block/FireBlock.java
+@@ -159,7 +159,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, Boolean.valueOf(this.canBurn(level.getBlockState(pos.relative(direction)))));
+@@ -331,7 +331,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;
+ }
+@@ -346,7 +346,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 8eb24bf0477c8d0cf4bfa7f122bbcd20aa2a5d5b..3035455cd9d8c238e33d493935a8498687d6a303 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 a97b22e5acb791e959c528ccb330fa5ff92251e4..ca1a1661642a8ddd66d0e180be996a978a150730 100644
+--- a/net/minecraft/world/level/block/LeavesBlock.java
++++ b/net/minecraft/world/level/block/LeavesBlock.java
+@@ -117,7 +117,7 @@ public class LeavesBlock extends Block implements SimpleWaterloggedBlock {
+ 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 41802482875bd4d4b505eb758740140de0db415a..8301a6d0fa0eeefc527aa79933a83da29ebaf451 100644
+--- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java
++++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java
+@@ -150,7 +150,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;
+ }
+@@ -161,7 +161,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 60d34309f6a961448944faedafd242e7496d14d9..e6cceee73b41604d3e034ad2a7edf7e281cf017b 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 6d7c2c6f0469f934b9599b59f00580cd1919be58..c868fbf333d59bee5256c07f7878c2dc6462a637 100644
+--- a/net/minecraft/world/level/levelgen/feature/Feature.java
++++ b/net/minecraft/world/level/levelgen/feature/Feature.java
+@@ -205,7 +205,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 bd17dbe53c82001b60f4cfdd9d2dacb06a98f5ea..59957ddf6b7742c0a8eac53eddb1b9fa0c7a7bec 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 256e130ff43f826c3eac2a0faa526dc12e5fda0e..c744ea5d2df9692e7e8c53ea96fedc8a2b77c0c9 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), Boolean.valueOf(true)), 2
+diff --git a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java
+index 75ae614790d46ba01003a13fb9d521299c675c56..ccb5a5d7536d4a3b57e08ff81a06784aed863dc7 100644
+--- a/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java
++++ b/net/minecraft/world/level/levelgen/feature/treedecorators/CreakingHeartDecorator.java
+@@ -37,7 +37,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 03ec2264b19e1794b609fe09d1ceaba4e0c4d669..3f38fe0140d13c7c356340ba06b55469ede0a1ad 100644
--- a/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java
@@ -247,3 +490,68 @@ index 03ec2264b19e1794b609fe09d1ceaba4e0c4d669..3f38fe0140d13c7c356340ba06b55469
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 1acd506f7c0679fa9f69b6ab221002b28d00c3e5..20502112f4e338984efc264109d0c894b1d33d56 100644
+--- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java
++++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java
+@@ -565,7 +565,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 b70ef62b4e41ee4851b2cdf53e0be1083233d052..f1ac1e1283cb276e5b21330d700cafdb65e60be1 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 d66321acb26682a02efa02cf1443b40d2a17f67b..86ed35984760a0a90e92a297d3080378e8b4bf6f 100644
+--- a/net/minecraft/world/level/material/LavaFluid.java
++++ b/net/minecraft/world/level/material/LavaFluid.java
+@@ -126,7 +126,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/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch
new file mode 100644
index 00000000..64550a29
--- /dev/null
+++ b/leaf-server/minecraft-patches/features/0192-Paper-Fix-infinite-loop-in-RegionFile-IO.patch
@@ -0,0 +1,19 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Taiyou06
+Date: Mon, 9 Jun 2025 12:00:57 +0200
+Subject: [PATCH] Paper: Fix infinite loop in RegionFile IO
+
+
+diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
+index 60ed8cff397c964323fbda203ebfab3c7c9a873b..32ee1d3a0ae67738a65545e6a0046a12fb940fa4 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) {
+ // need to re-try from the start
+ this.scheduleReadIO();
+ return;
diff --git a/public/readme/README_CN.md b/public/readme/README_CN.md
new file mode 100644
index 00000000..94fee8df
--- /dev/null
+++ b/public/readme/README_CN.md
@@ -0,0 +1,141 @@
+
+
+
+[](https://www.leafmc.one/zh/download)⠀
+[](https://github.com/Winds-Studio/Leaf/actions)⠀
+
+[](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)**,以获得更好的性能
+- **异步**寻路、生物生成和实体追踪
+- **大量优化**融合自 [其他核心](https://github.com/Winds-Studio/Leaf#-致谢) 的补丁, 和我们自己的
+- **完全兼容** Spigot 和 Paper 插件
+- **最新依赖**,保持所有依赖项为最新版本
+- **允许用户名使用所有字符**,包括中文和其他字符
+- **修复**一些 Minecraft 的 bug
+- **模组协议**支持
+- **更多自定义配置项**,源自 [Purpur](https://github.com/PurpurMC/Purpur) 的特性
+- **线性区域文件格式**,节省磁盘空间
+- **运维友好**,集成 [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) 的 [Sentry](https://sentry.io/welcome/),轻松详细追踪服务器的所有报错
+- 以及更多...
+
+## 📈 bStats 统计
+[](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
+
+
+
+## 🔥 特别感谢
+
+剑客云 | 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 通过创新和智能的工具支持开源项目,用于监控和分析 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/settings.gradle.kts b/settings.gradle.kts
index 124b899f..fd2d4a01 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -8,7 +8,7 @@ pluginManagement {
}
plugins {
- id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "leaf"