9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00

Update changes from ver/1.21.4 branch

This commit is contained in:
Dreeam
2025-05-02 22:45:26 -04:00
84 changed files with 6041 additions and 1028 deletions

View File

@@ -82,15 +82,6 @@ jobs:
- name: Create MojmapPaperclipJar - name: Create MojmapPaperclipJar
run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon run: ./gradlew createMojmapPaperclipJar --stacktrace --parallel --no-daemon
- name: Publish API
continue-on-error: true
run: |
./gradlew :leaf-api:publish
./gradlew publishDevBundlePublicationToLeafRepository -PpublishDevBundle=true
env:
REPO_USER: ${{ secrets.REPO_USER }}
REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }}
- name: Rename Paperclip JARs - name: Rename Paperclip JARs
run: | run: |
mv leaf-server/build/libs/leaf-paperclip-1.21.5-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.5-${{ env.BUILD_NUMBER }}.jar mv leaf-server/build/libs/leaf-paperclip-1.21.5-R0.1-SNAPSHOT-mojmap.jar ./leaf-1.21.5-${{ env.BUILD_NUMBER }}.jar

View File

@@ -14,12 +14,11 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@main uses: actions/checkout@main
- name: Set up GraalVM JDK 21 - name: Setup java
uses: graalvm/setup-graalvm@main uses: useblacksmith/setup-java@v5
with: with:
java-version: 21 distribution: 'temurin'
github-token: "${{ secrets.GITHUB_TOKEN }}" java-version: '21'
cache: gradle
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew

54
.github/workflows/publish-api.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Publish API
on:
push:
branches: [ "ver/1.21.4" ]
jobs:
build:
runs-on: ubuntu-latest
env:
BUILD_NUMBER: ${{ github.run_number }}
GRADLE_MEMORY: "-Xmx4g -XX:MaxMetaspaceSize=2g"
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Set up GraalVM JDK 21
uses: graalvm/setup-graalvm@main
with:
java-version: 21
github-token: "${{ secrets.GITHUB_TOKEN }}"
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Configure Git
run: |
git config --global user.email "no-reply@github.com"
git config --global user.name "Github Actions"
- name: Apply patches
run: |
PARALLELISM=$(($(nproc) * 2))
./gradlew -Dorg.gradle.jvmargs="${{ env.GRADLE_MEMORY }}" \
-Dleaf.patcher.parallelism=$PARALLELISM \
-Dleaf.patcher.incremental=true \
applyAllPatches \
--stacktrace --parallel \
--max-workers=$PARALLELISM \
--build-cache \
--no-daemon
- name: Build
run: ./gradlew build -x test
- name: Publish API
continue-on-error: true
run: |
./gradlew :leaf-api:publish
./gradlew publishDevBundlePublicationToLeafRepository -PpublishDevBundle=true
env:
REPO_USER: ${{ secrets.REPO_USER }}
REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }}

View File

@@ -4,7 +4,7 @@
[![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) [![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)
[![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) [![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) [![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-docs.leafmc.one-blue?label=docs&style=for-the-badge&colorA=19201a&colorB=298046)](https://docs.leafmc.one) [![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/)
**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 customized and high-performance.
</div> </div>
@@ -42,7 +42,7 @@ You can find the latest successful build in [GitHub Action](https://github.com/W
**Please note Java >= 21 is required.** **Please note Java >= 21 is required.**
## 📄 Documentation ## 📄 Documentation
Documentation on how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs/) Documentation on how to use/configure Leaf: [www.leafmc.one/docs](https://www.leafmc.one/docs)
## 📦 Building ## 📦 Building
Building a Paperclip JAR for distribution: Building a Paperclip JAR for distribution:
@@ -118,6 +118,7 @@ If these excellent projects hadn't appeared, Leaf wouldn't have become great.
<a href="https://github.com/LuminolMC/Luminol">Luminol</a><br> <a href="https://github.com/LuminolMC/Luminol">Luminol</a><br>
<a href="https://github.com/Gensokyo-Reimagined/Nitori">Nitori</a><br> <a href="https://github.com/Gensokyo-Reimagined/Nitori">Nitori</a><br>
<a href="https://github.com/Tuinity/Moonrise">Moonrise</a> (during 1.21.1)<br> <a href="https://github.com/Tuinity/Moonrise">Moonrise</a> (during 1.21.1)<br>
<a href="https://github.com/Samsuik/Sakura">Sakura</a><br>
</p> </p>
</details> </details>

View File

@@ -1,6 +1,8 @@
# This file is auto generated, any changes may be overridden! # This file is auto generated, any changes may be overridden!
# See CONTRIBUTING.md on how to add access transformers. # See CONTRIBUTING.md on how to add access transformers.
protected net.minecraft.world.entity.Entity dimensions 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 protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape
public net.minecraft.server.players.PlayerList SEND_PLAYER_INFO_INTERVAL public net.minecraft.server.players.PlayerList SEND_PLAYER_INFO_INTERVAL
public net.minecraft.util.Mth SIN public net.minecraft.util.Mth SIN

View File

@@ -13,16 +13,17 @@
val apiAndDocs: Configuration by configurations.creating { val apiAndDocs: Configuration by configurations.creating {
attributes { attributes {
@@ -41,9 +_,11 @@ @@ -41,9 +_,13 @@
dependencies { dependencies {
// api dependencies are listed transitively to API consumers // api dependencies are listed transitively to API consumers
- api("com.google.guava:guava:33.3.1-jre") - api("com.google.guava:guava:33.3.1-jre")
- api("com.google.code.gson:gson:2.11.0")
- api("org.yaml:snakeyaml:2.2")
+ // Leaf start - Bump Dependencies + // Leaf start - Bump Dependencies
+ api("com.google.guava:guava:33.4.0-jre") + api("com.google.guava:guava:33.4.0-jre")
+ api("com.google.code.gson:gson:2.12.1") + // Waiting Paper, Gson has breaking change since 2.12.0
+ // See https://github.com/google/gson/commit/6c2e3db7d25ceceabe056aeb8b65477fdd509214
api("com.google.code.gson:gson:2.11.0")
- api("org.yaml:snakeyaml:2.2")
+ api("org.yaml:snakeyaml:2.3") // 2.4 removed `org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder` + api("org.yaml:snakeyaml:2.3") // 2.4 removed `org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder`
+ // Leaf end - Bump Dependencies + // Leaf end - Bump Dependencies
api("org.joml:joml:1.10.8") { api("org.joml:joml:1.10.8") {

View File

@@ -4,12 +4,40 @@ Date: Wed, 19 Feb 2025 00:34:16 -0500
Subject: [PATCH] PlayerInventoryOverflowEvent Subject: [PATCH] PlayerInventoryOverflowEvent
diff --git a/src/main/java/org/bukkit/event/HandlerList.java b/src/main/java/org/bukkit/event/HandlerList.java
index ef9d9f3ddaf25aa990715139e1916672f76c35e5..97b591ab30d6e0af4518b71fefcf9c8f3c050176 100644
--- a/src/main/java/org/bukkit/event/HandlerList.java
+++ b/src/main/java/org/bukkit/event/HandlerList.java
@@ -64,6 +64,7 @@ public class HandlerList {
h.handlers = null;
}
}
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = -1; // Leaf - PlayerInventoryOverflowEvent
}
}
@@ -77,6 +78,7 @@ public class HandlerList {
for (HandlerList h : allLists) {
h.unregister(plugin);
}
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = -1; // Leaf - PlayerInventoryOverflowEvent
}
}
@@ -90,6 +92,7 @@ public class HandlerList {
for (HandlerList h : allLists) {
h.unregister(listener);
}
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = -1; // Leaf - PlayerInventoryOverflowEvent
}
}
diff --git a/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java b/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java diff --git a/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java b/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..44c65eb6c503b94ac73d2b2169359be1b4810c98 index 0000000000000000000000000000000000000000..eae76671190ef84529c0dd503263e43a15a74e8a
--- /dev/null --- /dev/null
+++ b/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java +++ b/src/main/java/org/dreeam/leaf/event/player/PlayerInventoryOverflowEvent.java
@@ -0,0 +1,63 @@ @@ -0,0 +1,65 @@
+package org.dreeam.leaf.event.player; +package org.dreeam.leaf.event.player;
+ +
+import org.bukkit.entity.Player; +import org.bukkit.entity.Player;
@@ -36,6 +64,8 @@ index 0000000000000000000000000000000000000000..44c65eb6c503b94ac73d2b2169359be1
+ +
+ private static final HandlerList HANDLER_LIST = new HandlerList(); + private static final HandlerList HANDLER_LIST = new HandlerList();
+ +
+ public static short isListeningInvOverflowCached = -1;
+
+ private final Inventory inventory; + private final Inventory inventory;
+ private final Map<Integer, ItemStack> overflowItemStacks; + private final Map<Integer, ItemStack> overflowItemStacks;
+ +

View File

@@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Sat, 12 Apr 2025 12:41:08 -0400
Subject: [PATCH] PaperPR: Fix save/load NaN Entity Motion
Removed since Paper 1.21.5, added on Paper
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/12269
Fix Paper#12262 using like the same logic than "pitch/yaw" for set to 0 when a value is NaN
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index f114a8f5143799d72e36e0a535888c5fb25213e1..a81983182ee3e3b874ba83ddf9bbc6ea772a2997 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -2474,6 +2474,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
// CraftBukkit end
+ this.setDeltaMovement(io.papermc.paper.util.MCUtil.sanitizeNanInf(this.deltaMovement, 0D)); // Paper - remove NaN values before usage in saving
Vec3 deltaMovement = this.getDeltaMovement();
compound.put("Motion", this.newDoubleList(deltaMovement.x, deltaMovement.y, deltaMovement.z));
// CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero
@@ -2620,9 +2621,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
ListTag list = compound.getList("Pos", 6);
ListTag list1 = compound.getList("Motion", 6);
ListTag list2 = compound.getList("Rotation", 5);
- double _double = list1.getDouble(0);
- double _double1 = list1.getDouble(1);
- double _double2 = list1.getDouble(2);
+ // Paper start - avoid setting NaN values
+ double _double = list1.getDouble(0); _double = io.papermc.paper.util.MCUtil.sanitizeNanInf(_double, 0D);
+ double _double1 = list1.getDouble(1); _double1 = io.papermc.paper.util.MCUtil.sanitizeNanInf(_double1, 0D);
+ double _double2 = list1.getDouble(2); _double2 = io.papermc.paper.util.MCUtil.sanitizeNanInf(_double2, 0D);
+ // Paper end - avoid setting NaN values
this.setDeltaMovement(
Math.abs(_double) > 10.0 ? 0.0 : _double, Math.abs(_double1) > 10.0 ? 0.0 : _double1, Math.abs(_double2) > 10.0 ? 0.0 : _double2
);

View File

@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dqu1J <mcpeytwot5@gmail.com>
Date: Tue, 29 Apr 2025 01:35:45 -0400
Subject: [PATCH] PaperPR: Fix unnecessary map data saves
Removed since Paper 1.21.5, added on Paper
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/12296
Fixes Paper#12295
Currently, MapCanvas API unnecessarily causes map data file to save.
The API calls this.mapView.worldMap.setColorsDirty() to mark colors dirty for players, however this has the side-effect of saving the map .dat files as well:
```
public void setColorsDirty(int x, int z) {
this.setDirty(); // This saves the data file!
for (MapItemSavedData.HoldingPlayer holdingPlayer : this.carriedBy) {
holdingPlayer.markColorsDirty(x, z); // This is what the API wants to do!
}
}
```
This causes unnecessary lag during world saves, which scales with the amount of maps handled with API since last save. On servers that heavily rely on imageonmap-like plugins, it can cause lag spikes of dozens of seconds.
This PR changes this method to add a boolean argument to the method that determines whether the file is saved or not, which is used by the API. An overload is added for compatibility, and for vanilla.
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 59829bb134555d96edcf4cbb844ccacb88c44961..76916ac06208348f718dffc9be232feb66b84d5c 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -456,7 +456,12 @@ public class MapItemSavedData extends SavedData {
}
public void setColorsDirty(int x, int z) {
- this.setDirty();
+ // Paper start - Fix unnecessary map data saves
+ this.setColorsDirty(x, z, true);
+ }
+ public void setColorsDirty(int x, int z, boolean markFileDirty) {
+ if (markFileDirty) this.setDirty();
+ // Paper end - Fix unnecessary map data saves
for (MapItemSavedData.HoldingPlayer holdingPlayer : this.carriedBy) {
holdingPlayer.markColorsDirty(x, z);

View File

@@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Parallel world ticking
Original project: https://github.com/SparklyPower/SparklyPaper Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index d6a30d6735d24f24a8108b6a5d15725587bb662a..39517966935265bc4533d4ce414d2df72df5a614 100644 index be820c6093dd2ae7642b9bee11edf65e3a8d7242..06ac3537f5655d048d770bb004243f207fad9faa 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -1050,7 +1050,7 @@ public final class ChunkHolderManager { @@ -1031,7 +1031,7 @@ public final class ChunkHolderManager {
if (changedFullStatus.isEmpty()) { if (changedFullStatus.isEmpty()) {
return; return;
} }
@@ -18,7 +18,7 @@ index d6a30d6735d24f24a8108b6a5d15725587bb662a..39517966935265bc4533d4ce414d2df7
this.taskScheduler.scheduleChunkTask(() -> { this.taskScheduler.scheduleChunkTask(() -> {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate; final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1076,7 +1076,12 @@ public final class ChunkHolderManager { @@ -1057,7 +1057,12 @@ public final class ChunkHolderManager {
// note: never call while inside the chunk system, this will absolutely break everything // note: never call while inside the chunk system, this will absolutely break everything
public void processUnloads() { public void processUnloads() {
@@ -32,7 +32,7 @@ index d6a30d6735d24f24a8108b6a5d15725587bb662a..39517966935265bc4533d4ce414d2df7
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) { if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot unload chunks recursively"); throw new IllegalStateException("Cannot unload chunks recursively");
@@ -1358,7 +1363,7 @@ public final class ChunkHolderManager { @@ -1339,7 +1344,7 @@ public final class ChunkHolderManager {
List<NewChunkHolder> changedFullStatus = null; List<NewChunkHolder> changedFullStatus = null;
@@ -650,7 +650,7 @@ index ae5d3de44fb710b48fdabf04f5e706df1f9889b7..31abf2da10bc9b4b7825ed4b3d4e9da5
// Paper start - extra debug info // Paper start - extra debug info
if (entity.valid) { if (entity.valid) {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index e2b15968e89a532ec21c786f41b7f9322fd65a04..915498dcb08a678256e6cc1659642d6126851365 100644 index fefaab58da149b082a4d1e3bed9ec84ae8488d45..9100da3fe4e478cea7198cb4e028fcefccb3eb3c 100644
--- a/net/minecraft/server/level/ServerPlayer.java --- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java
@@ -434,6 +434,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -434,6 +434,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -669,7 +669,7 @@ index e2b15968e89a532ec21c786f41b7f9322fd65a04..915498dcb08a678256e6cc1659642d61
// CraftBukkit start // CraftBukkit start
if (this.joining) { if (this.joining) {
this.joining = false; this.joining = false;
@@ -1454,6 +1456,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -1455,6 +1457,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
teleportTransition.postTeleportTransition().onTransition(this); teleportTransition.postTeleportTransition().onTransition(this);
return this; return this;
} else { } else {
@@ -678,7 +678,7 @@ index e2b15968e89a532ec21c786f41b7f9322fd65a04..915498dcb08a678256e6cc1659642d61
// CraftBukkit start // CraftBukkit start
/* /*
this.isChangingDimension = true; this.isChangingDimension = true;
@@ -1825,6 +1829,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -1826,6 +1830,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return OptionalInt.empty(); return OptionalInt.empty();
} else { } else {
// CraftBukkit start // CraftBukkit start
@@ -691,7 +691,7 @@ index e2b15968e89a532ec21c786f41b7f9322fd65a04..915498dcb08a678256e6cc1659642d61
this.containerMenu = abstractContainerMenu; // Moved up this.containerMenu = abstractContainerMenu; // Moved up
if (!this.isImmobile()) if (!this.isImmobile())
this.connection this.connection
@@ -1889,6 +1899,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @@ -1890,6 +1900,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
} }
@Override @Override
public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
@@ -743,10 +743,10 @@ index b17c8a2f5294ac28cc05fb05c84a041b2c6c8721..0b8b4658dbbad1bacc13e97b4fc0cdce
serverPlayer.connection = player.connection; serverPlayer.connection = player.connection;
serverPlayer.restoreFrom(player, keepInventory); serverPlayer.restoreFrom(player, keepInventory);
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 9bc978ca290ca772b0367e89b69fe16b502b0cd2..17ff57db6f90b3f4facef691ad4347e67a9f5836 100644 index 9bc978ca290ca772b0367e89b69fe16b502b0cd2..334a47659ba75fade062bc79df3731d1e449114b 100644
--- a/net/minecraft/world/entity/Entity.java --- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java
@@ -3370,15 +3370,33 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -3370,15 +3370,40 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
if (this.portalProcess != null) { if (this.portalProcess != null) {
if (this.portalProcess.processPortalTeleportation(serverLevel, this, this.canUsePortal(false))) { if (this.portalProcess.processPortalTeleportation(serverLevel, this, this.canUsePortal(false))) {
this.setPortalCooldown(); this.setPortalCooldown();
@@ -760,6 +760,11 @@ index 9bc978ca290ca772b0367e89b69fe16b502b0cd2..17ff57db6f90b3f4facef691ad4347e6
+ // TCRF SparklyPaper (Pathothingi) start - parallel world ticking + // TCRF SparklyPaper (Pathothingi) start - parallel world ticking
+ java.util.function.Consumer<Entity> portalEntityTask = entity -> { + java.util.function.Consumer<Entity> portalEntityTask = entity -> {
+ assert entity.portalProcess != null; + assert entity.portalProcess != null;
+ // Fix NPE when portalProcess becomes null before task execution
+ // Portal process was likely nulled out (e.g., expired) between scheduling and execution.
+ if (entity.portalProcess == null) {
+ return;
+ }
+ +
+ if (entity.portalProcess.isParallelCancelledByPlugin()) { + if (entity.portalProcess.isParallelCancelledByPlugin()) {
+ entity.portalProcess = null; + entity.portalProcess = null;
@@ -775,8 +780,10 @@ index 9bc978ca290ca772b0367e89b69fe16b502b0cd2..17ff57db6f90b3f4facef691ad4347e6
+ entity.teleport(portalDestination); + entity.teleport(portalDestination);
+ } + }
+ } + }
+ if (this.portalProcess != null) + // Add another null check here just in case teleport() somehow nulled it (defensive)
+ if (entity.portalProcess != null) {
+ entity.portalProcess.confirmParallelAsHandled(); + entity.portalProcess.confirmParallelAsHandled();
+ }
+ }; + };
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ this.portalProcess.setParallelAsScheduled(); + this.portalProcess.setParallelAsScheduled();
@@ -787,7 +794,7 @@ index 9bc978ca290ca772b0367e89b69fe16b502b0cd2..17ff57db6f90b3f4facef691ad4347e6
} else if (this.portalProcess.hasExpired()) { } else if (this.portalProcess.hasExpired()) {
this.portalProcess = null; this.portalProcess = null;
} }
@@ -3908,6 +3926,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -3908,6 +3933,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
} }
private Entity teleportCrossDimension(ServerLevel level, TeleportTransition teleportTransition) { private Entity teleportCrossDimension(ServerLevel level, TeleportTransition teleportTransition) {

View File

@@ -0,0 +1,69 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Thu, 1 May 2025 22:38:48 +0200
Subject: [PATCH] Sakura: Optimise-check-inside-blocks-and-traverse-blocks
Dreeam TODO: refactor checkinsideblcoks
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 4221e5322fa3a3ff6ab53946aa71d54144d2c4b2..4474639b0411236e208c542973084864365c544c 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1670,6 +1670,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
private void checkInsideBlocks(List<Entity.Movement> movements, Set<BlockState> blocksInside) {
if (this.isAffectedByBlocks()) {
LongSet set = this.visitedBlocks;
+ // Sakura start - optimise check inside blocks
+ int lastChunkX = Integer.MIN_VALUE;
+ int lastChunkZ = Integer.MIN_VALUE;
+ net.minecraft.world.level.chunk.ChunkAccess chunk = null;
+ // Sakura end - optimise check inside blocks
for (Entity.Movement movement : movements) {
Vec3 vec3 = movement.from();
@@ -1681,7 +1686,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return;
}
- BlockState blockState = this.level().getBlockState(blockPos);
+ // Sakura start - optimise check inside blocks
+ final int chunkX = blockPos.getX() >> 4;
+ final int chunkZ = blockPos.getZ() >> 4;
+ if (chunk == null || chunkX != lastChunkX || chunkZ != lastChunkZ) {
+ chunk = this.level.getChunkIfLoadedImmediately(chunkX, chunkZ);
+ if (chunk == null) {
+ continue;
+ }
+ lastChunkX = chunkX;
+ lastChunkZ = chunkZ;
+ }
+ final BlockState blockState = chunk.getBlockState(blockPos);
+ // Sakura end - optimise check inside blocks
if (!blockState.isAir() && set.add(blockPos.asLong())) {
try {
VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos);
diff --git a/net/minecraft/world/level/BlockGetter.java b/net/minecraft/world/level/BlockGetter.java
index 91865d7e78e15cc643a65de03045b90a52d6ec2a..03f82e60528738e89f195cfc59094f53156f5370 100644
--- a/net/minecraft/world/level/BlockGetter.java
+++ b/net/minecraft/world/level/BlockGetter.java
@@ -214,10 +214,18 @@ public interface BlockGetter extends LevelHeightAccessor {
static Iterable<BlockPos> boxTraverseBlocks(Vec3 oldPosition, Vec3 position, AABB boundingBox) {
Vec3 vec3 = position.subtract(oldPosition);
- Iterable<BlockPos> iterable = BlockPos.betweenClosed(boundingBox);
+ // Sakura start - optimise check inside blocks
if (vec3.lengthSqr() < Mth.square(0.99999F)) {
- return iterable;
+ return org.dreeam.leaf.util.map.BlockPosIterator.iterable(boundingBox);
} else {
+ final boolean xZero = vec3.x() == 0.0;
+ final boolean yZero = vec3.y() == 0.0;
+ final boolean zZero = vec3.z() == 0.0;
+ if (xZero && yZero || yZero && zZero || xZero && zZero) {
+ return org.dreeam.leaf.util.map.BlockPosIterator.traverseArea(vec3, boundingBox);
+ }
+ Iterable<BlockPos> iterable = BlockPos.betweenClosed(boundingBox);
+ // Sakura end - optimise check inside blocks
Set<BlockPos> set = new ObjectLinkedOpenHashSet<>();
Vec3 minPosition = boundingBox.getMinPosition();
Vec3 vec31 = minPosition.subtract(vec3);

View File

@@ -238,24 +238,16 @@ index a4aa2615823d77920ff55b8aa0bcc27a54b8c3e1..2fb65ce228da94eb7d9364ee0f945823
+ // SparklyPaper end - parallel world ticking + // 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 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 196fddeab452e7bc89ef6758635e1d07074e7416..55a2ffa0fff6ef66b9bd5069300c09e9e3535c0d 100644 index 548fcd9646dee0c40b6ba9b3dafb9ca157dfe324..d7af94890bfccd6ff665d920cecfa1e5be626aa4 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java --- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -28,6 +28,7 @@ import java.util.logging.Level; @@ -40,6 +40,12 @@ class PaperEventManager {
class PaperEventManager {
private final Server server;
+ private final org.purpurmc.purpur.util.MinecraftInternalPlugin minecraftInternalPlugin = new org.purpurmc.purpur.util.MinecraftInternalPlugin(); // Leaf - Parallel world ticking
public PaperEventManager(Server server) {
this.server = server;
@@ -40,6 +41,12 @@ class PaperEventManager {
if (listeners.length == 0) return; if (listeners.length == 0) return;
// Leaf end - Skip event if no listeners // Leaf end - Skip event if no listeners
if (event.isAsynchronous() && this.server.isPrimaryThread()) { if (event.isAsynchronous() && this.server.isPrimaryThread()) {
+ // Leaf start - Parallel world ticking + // Leaf start - Parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) { + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) {
+ org.bukkit.Bukkit.getAsyncScheduler().runNow(minecraftInternalPlugin, task -> event.callEvent()); + org.dreeam.leaf.async.world.PWTEventScheduler.getScheduler().scheduleTask(event::callEvent);
+ return; + return;
+ } + }
+ // Leaf end - Parallel world ticking + // Leaf end - Parallel world ticking
@@ -263,10 +255,10 @@ index 196fddeab452e7bc89ef6758635e1d07074e7416..55a2ffa0fff6ef66b9bd5069300c09e9
} else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) {
// Leaf start - Multithreaded tracker // Leaf start - Multithreaded tracker
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fee27b6476 100644 index af33cab59932f4ec135caf94dc5828930833daf6..92463ddc6fdcf542ce4a6d2a5059d4a9f7a6085a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -456,7 +456,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -455,7 +455,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
} }
private boolean unloadChunk0(int x, int z, boolean save) { private boolean unloadChunk0(int x, int z, boolean save) {
@@ -280,7 +272,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
if (!this.isChunkLoaded(x, z)) { if (!this.isChunkLoaded(x, z)) {
return true; return true;
} }
@@ -473,6 +478,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -472,6 +477,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public boolean refreshChunk(int x, int z) { public boolean refreshChunk(int x, int z) {
@@ -289,7 +281,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
if (playerChunk == null) return false; if (playerChunk == null) return false;
@@ -523,7 +530,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -522,7 +529,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public boolean loadChunk(int x, int z, boolean generate) { public boolean loadChunk(int x, int z, boolean generate) {
@@ -303,7 +295,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
@@ -751,6 +763,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -750,6 +762,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) { public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
@@ -312,7 +304,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
this.world.captureTreeGeneration = true; this.world.captureTreeGeneration = true;
this.world.captureBlockStates = true; this.world.captureBlockStates = true;
boolean grownTree = this.generateTree(loc, type); boolean grownTree = this.generateTree(loc, type);
@@ -866,6 +880,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -865,6 +879,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
} }
public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer<net.minecraft.world.level.ServerExplosion> configurator) { public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer<net.minecraft.world.level.ServerExplosion> configurator) {
// Paper end - expand explosion API // Paper end - expand explosion API
@@ -321,7 +313,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
net.minecraft.world.level.Level.ExplosionInteraction explosionType; net.minecraft.world.level.Level.ExplosionInteraction explosionType;
if (!breakBlocks) { if (!breakBlocks) {
explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks
@@ -957,6 +973,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -956,6 +972,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) { public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
@@ -330,7 +322,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
warnUnsafeChunk("getting a faraway chunk", x >> 4, z >> 4); // Paper warnUnsafeChunk("getting a faraway chunk", x >> 4, z >> 4); // Paper
// Transient load for this tick // Transient load for this tick
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z); return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
@@ -987,6 +1005,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -986,6 +1004,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> bb) { public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> bb) {
BlockPos pos = new BlockPos(x, 0, z); BlockPos pos = new BlockPos(x, 0, z);
@@ -339,7 +331,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
if (this.world.hasChunkAt(pos)) { if (this.world.hasChunkAt(pos)) {
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos); net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos);
@@ -2329,6 +2349,8 @@ public class CraftWorld extends CraftRegionAccessor implements World { @@ -2328,6 +2348,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override @Override
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) { public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
@@ -349,7 +341,7 @@ index 6bcece7ceb5be047371faf7ab85b3688ed3e045b..96faa72abe918e7ab3f1dec44072a5fe
} }
// Paper end // Paper end
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56bead15b24 100644 index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..b94efcab12d41fa8745e5bb55cfa6481d8262e74 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -75,6 +75,11 @@ public class CraftBlock implements Block { @@ -75,6 +75,11 @@ public class CraftBlock implements Block {
@@ -364,31 +356,32 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
return this.world.getBlockState(this.position); return this.world.getBlockState(this.position);
} }
@@ -155,6 +160,11 @@ public class CraftBlock implements Block { @@ -157,6 +162,11 @@ public class CraftBlock implements Block {
} }
private void setData(final byte data, int flags) { private void setData(final byte data, int flag) {
+ // SparklyPaper start - parallel world ticking + // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable) + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously"); + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ } + }
+ // SparklyPaper end - parallel world ticking + // SparklyPaper end - parallel world ticking
this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flags); this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flag);
} }
@@ -196,6 +206,11 @@ public class CraftBlock implements Block { @@ -198,6 +208,12 @@ public class CraftBlock implements Block {
} }
public static boolean setBlockState(LevelAccessor world, BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, boolean applyPhysics) { public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) {
+ // SparklyPaper start - parallel world ticking + // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable) + if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, pos, "Cannot modify world asynchronously"); + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ } + }
+ // SparklyPaper end - parallel world ticking + // SparklyPaper end - parallel world ticking
// SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in block entity cleanup +
if (oldState.hasBlockEntity() && newState.getBlock() != oldState.getBlock()) { // SPIGOT-3725 remove old block entity if block changes // SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup
if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes
// SPIGOT-4612: faster - just clear tile // SPIGOT-4612: faster - just clear tile
@@ -344,18 +359,33 @@ public class CraftBlock implements Block { @@ -343,18 +359,33 @@ public class CraftBlock implements Block {
@Override @Override
public Biome getBiome() { public Biome getBiome() {
@@ -422,7 +415,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio); this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
} }
@@ -376,6 +406,11 @@ public class CraftBlock implements Block { @@ -375,6 +406,11 @@ public class CraftBlock implements Block {
@Override @Override
public boolean isBlockIndirectlyPowered() { public boolean isBlockIndirectlyPowered() {
@@ -434,7 +427,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
return this.world.getMinecraftWorld().hasNeighborSignal(this.position); return this.world.getMinecraftWorld().hasNeighborSignal(this.position);
} }
@@ -415,6 +450,11 @@ public class CraftBlock implements Block { @@ -414,6 +450,11 @@ public class CraftBlock implements Block {
@Override @Override
public int getBlockPower(BlockFace face) { public int getBlockPower(BlockFace face) {
@@ -446,7 +439,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
int power = 0; int power = 0;
net.minecraft.world.level.Level world = this.world.getMinecraftWorld(); net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
int x = this.getX(); int x = this.getX();
@@ -483,6 +523,11 @@ public class CraftBlock implements Block { @@ -484,6 +525,11 @@ public class CraftBlock implements Block {
@Override @Override
public boolean breakNaturally() { public boolean breakNaturally() {
@@ -458,7 +451,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
return this.breakNaturally(null); return this.breakNaturally(null);
} }
@@ -542,6 +587,11 @@ public class CraftBlock implements Block { @@ -543,6 +589,11 @@ public class CraftBlock implements Block {
@Override @Override
public boolean applyBoneMeal(BlockFace face) { public boolean applyBoneMeal(BlockFace face) {
@@ -470,18 +463,18 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
Direction direction = CraftBlock.blockFaceToNotch(face); Direction direction = CraftBlock.blockFaceToNotch(face);
BlockFertilizeEvent event = null; BlockFertilizeEvent event = null;
ServerLevel world = this.getCraftWorld().getHandle(); ServerLevel world = this.getCraftWorld().getHandle();
@@ -553,8 +603,8 @@ public class CraftBlock implements Block { @@ -554,8 +605,8 @@ public class CraftBlock implements Block {
world.captureTreeGeneration = false; world.captureTreeGeneration = false;
if (!world.capturedBlockStates.isEmpty()) { if (world.capturedBlockStates.size() > 0) {
- TreeType treeType = SaplingBlock.treeType; - TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null; - SaplingBlock.treeType = null;
+ TreeType treeType = SaplingBlock.getTreeTypeRT(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + TreeType treeType = SaplingBlock.getTreeTypeRT(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ SaplingBlock.setTreeTypeRT(null); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + SaplingBlock.setTreeTypeRT(null); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
List<BlockState> states = new ArrayList<>(world.capturedBlockStates.values()); List<BlockState> blocks = new ArrayList<>(world.capturedBlockStates.values());
world.capturedBlockStates.clear(); world.capturedBlockStates.clear();
StructureGrowEvent structureEvent = null; StructureGrowEvent structureEvent = null;
@@ -644,6 +694,11 @@ public class CraftBlock implements Block { @@ -644,6 +695,11 @@ public class CraftBlock implements Block {
@Override @Override
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) { public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
@@ -493,7 +486,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
Preconditions.checkArgument(start != null, "Location start cannot be null"); Preconditions.checkArgument(start != null, "Location start cannot be null");
Preconditions.checkArgument(this.getWorld().equals(start.getWorld()), "Location start cannot be a different world"); Preconditions.checkArgument(this.getWorld().equals(start.getWorld()), "Location start cannot be a different world");
start.checkFinite(); start.checkFinite();
@@ -685,6 +740,11 @@ public class CraftBlock implements Block { @@ -685,6 +741,11 @@ public class CraftBlock implements Block {
@Override @Override
public boolean canPlace(BlockData data) { public boolean canPlace(BlockData data) {
@@ -505,7 +498,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
Preconditions.checkArgument(data != null, "BlockData cannot be null"); Preconditions.checkArgument(data != null, "BlockData cannot be null");
net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState(); net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState();
net.minecraft.world.level.Level world = this.world.getMinecraftWorld(); net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
@@ -719,6 +779,11 @@ public class CraftBlock implements Block { @@ -719,6 +780,11 @@ public class CraftBlock implements Block {
@Override @Override
public void tick() { public void tick() {
@@ -518,7 +511,7 @@ index 5f04b2dcff7c0e967647bde9dfa0f35f59e8524c..8fa769f1f18d05b6f037c84175dcb56b
this.getNMS().tick(level, this.position, level.random); this.getNMS().tick(level, this.position, level.random);
} }
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index 3422970353dcd886934b9ee906467769d39abbde..13c91223bbb4841cab0e491037a36113a33faf15 100644 index 768d3f93da2522d467183654260a8bd8653588b1..5cef786fa2e5dfd3e7b79918bc73af02891b0bea 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -26,6 +26,27 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft @@ -26,6 +26,27 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
@@ -547,8 +540,8 @@ index 3422970353dcd886934b9ee906467769d39abbde..13c91223bbb4841cab0e491037a36113
+ } + }
+ // Leaf end - SparklyPaper - parallel world ticking mod + // Leaf end - SparklyPaper - parallel world ticking mod
public CraftBlockEntityState(World world, T blockEntity) { public CraftBlockEntityState(World world, T tileEntity) {
super(world, blockEntity.getBlockPos(), blockEntity.getBlockState()); super(world, tileEntity.getBlockPos(), tileEntity.getBlockState());
@@ -34,8 +55,8 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft @@ -34,8 +55,8 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
try { // Paper - Show blockstate location if we failed to read it try { // Paper - Show blockstate location if we failed to read it
@@ -557,14 +550,14 @@ index 3422970353dcd886934b9ee906467769d39abbde..13c91223bbb4841cab0e491037a36113
- if (DISABLE_SNAPSHOT) { - if (DISABLE_SNAPSHOT) {
+ this.snapshotDisabled = getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + this.snapshotDisabled = getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ if (this.snapshotDisabled) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + if (this.snapshotDisabled) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
this.snapshot = this.blockEntity; this.snapshot = this.tileEntity;
} else { } else {
this.snapshot = this.createSnapshot(blockEntity); this.snapshot = this.createSnapshot(tileEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
index c0a7659776a9f0fb82bb8563acbf3286b318fe03..69fc25b20cc84a2cd89cb0927aceba05df405e2a 100644 index fa63a6cfcfcc4eee4503a82d85333c139c8c8b2b..d106f65e4b745242484a195958fc559268a7dee0 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
@@ -217,6 +217,12 @@ public class CraftBlockState implements BlockState { @@ -215,6 +215,12 @@ public class CraftBlockState implements BlockState {
LevelAccessor access = this.getWorldHandle(); LevelAccessor access = this.getWorldHandle();
CraftBlock block = this.getBlock(); CraftBlock block = this.getBlock();
@@ -577,7 +570,7 @@ index c0a7659776a9f0fb82bb8563acbf3286b318fe03..69fc25b20cc84a2cd89cb0927aceba05
if (block.getType() != this.getType()) { if (block.getType() != this.getType()) {
if (!force) { if (!force) {
return false; return false;
@@ -365,6 +371,8 @@ public class CraftBlockState implements BlockState { @@ -350,6 +356,8 @@ public class CraftBlockState implements BlockState {
@Override @Override
public java.util.Collection<org.bukkit.inventory.ItemStack> getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) { public java.util.Collection<org.bukkit.inventory.ItemStack> getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) {
@@ -587,35 +580,37 @@ index c0a7659776a9f0fb82bb8563acbf3286b318fe03..69fc25b20cc84a2cd89cb0927aceba05
net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item); net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
index 321280d7c9c3c828cbf2eb19d2fd196a1f84d4c3..cd8f771e08cee5d00c53a8e70f0fe37cf393cd52 100644 index 55572e799b5c8a74a546ac8febc14f80d5731c52..08a06c23c831a4de45b3e537228b837911019da8 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
@@ -195,14 +195,14 @@ public final class CraftBlockStates { @@ -249,8 +249,8 @@ public final class CraftBlockStates {
BlockPos pos = craftBlock.getPosition(); net.minecraft.world.level.block.state.BlockState blockData = craftBlock.getNMS();
net.minecraft.world.level.block.state.BlockState state = craftBlock.getNMS(); BlockEntity tileEntity = craftBlock.getHandle().getBlockEntity(blockPosition);
BlockEntity blockEntity = craftBlock.getHandle().getBlockEntity(pos); // Paper start - block state snapshots
- boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT; - boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
- CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot; - CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
+ boolean prev = CraftBlockEntityState.getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + boolean prev = CraftBlockEntityState.getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ CraftBlockEntityState.setDisableSnapshotTL(!useSnapshot); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + CraftBlockEntityState.setDisableSnapshotTL(!useSnapshot); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
try { try {
CraftBlockState blockState = CraftBlockStates.getBlockState(world, pos, state, blockEntity); // Paper end
blockState.setWorldHandle(craftBlock.getHandle()); // Inject the block's generator access CraftBlockState blockState = CraftBlockStates.getBlockState(world, blockPosition, blockData, tileEntity);
return blockState; @@ -258,7 +258,7 @@ public final class CraftBlockStates {
return blockState;
// Paper start
} finally { } finally {
- CraftBlockEntityState.DISABLE_SNAPSHOT = prev; - CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
+ CraftBlockEntityState.setDisableSnapshotTL(prev); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior) + CraftBlockEntityState.setDisableSnapshotTL(prev); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
} }
// Paper end
} }
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index ebb07b9b9f6bef9195978c8ecdd5f4ef3ee198bc..3848011363056d700110564c6af8e4c3bd54ac6c 100644 index c2552c3706831f7012b5b449fa43c7d5990056a4..4e8a1d01a6c0afef92ae56cc4909af06d63e5268 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -818,6 +818,28 @@ public class CraftEventFactory { @@ -961,6 +961,28 @@ public class CraftEventFactory {
} }
public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPos up to five methods deep. public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
+ public static final ThreadLocal<BlockPos> sourceBlockOverrideRT = new ThreadLocal<>(); // SparklyPaper - parallel world ticking (this is from Folia, fixes concurrency bugs with sculk catalysts) + public static final ThreadLocal<BlockPos> sourceBlockOverrideRT = new ThreadLocal<>(); // SparklyPaper - parallel world ticking (this is from Folia, fixes concurrency bugs with sculk catalysts)
+ +
+ // Leaf start - SparklyPaper - parallel world ticking mod + // Leaf start - SparklyPaper - parallel world ticking mod
@@ -639,20 +634,29 @@ index ebb07b9b9f6bef9195978c8ecdd5f4ef3ee198bc..3848011363056d700110564c6af8e4c3
+ } + }
+ // Leaf end - SparklyPaper - parallel world ticking mod + // Leaf end - SparklyPaper - parallel world ticking mod
public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState state, int flags) { public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState block, int flag) {
return handleBlockSpreadEvent(world, source, target, state, flags, false); // Suppress during worldgen
@@ -833,7 +855,10 @@ public class CraftEventFactory { @@ -972,7 +994,10 @@ public class CraftEventFactory {
CraftBlockState snapshot = CraftBlockStates.getBlockState(world, target); CraftBlockState state = CraftBlockStates.getBlockState(world, target, flag);
snapshot.setData(state); state.setData(block);
- BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), snapshot); - BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), state);
+ // Leaf start - SparklyPaper parallel world ticking mod (collapse original behavior) + // Leaf start - SparklyPaper parallel world ticking mod (collapse original behavior)
+ final BlockPos sourceBlockOverrideRTSnap = getSourceBlockOverrideRT(); + final BlockPos sourceBlockOverrideRTSnap = getSourceBlockOverrideRT();
+ BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(world, sourceBlockOverrideRTSnap != null ? sourceBlockOverrideRTSnap : source), snapshot); // SparklyPaper - parallel world ticking + BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, sourceBlockOverrideRTSnap != null ? sourceBlockOverrideRTSnap : source), state); // SparklyPaper - parallel world ticking
+ // Leaf end - SparklyPaper parallel world ticking mod (collapse original behavior) + // Leaf end - SparklyPaper parallel world ticking mod (collapse original behavior)
if (event.callEvent()) { Bukkit.getPluginManager().callEvent(event);
boolean result = snapshot.place(flags);
return !checkSetResult || result; if (!event.isCancelled()) {
@@ -2265,7 +2290,7 @@ public class CraftEventFactory {
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemStack.copyWithCount(1));
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), CraftVector.toBukkit(to));
- if (!net.minecraft.world.level.block.DispenserBlock.eventFired) {
+ if (!net.minecraft.world.level.block.DispenserBlock.getEventFiredTL()) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
if (!event.callEvent()) {
return itemStack;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java 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 index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..e7c6b2ab5f2c68f3319ccd52785c8d3488a2eef7 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java

View File

@@ -0,0 +1,37 @@
package org.dreeam.leaf.async.world;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PWTEventScheduler {
private static volatile PWTEventScheduler instance;
private final ExecutorService executor;
private PWTEventScheduler() {
this.executor = Executors.newCachedThreadPool(
new ThreadFactoryBuilder()
.setNameFormat("Leaf PWT Event Scheduler Thread - %d")
.setDaemon(true)
.setPriority(Thread.NORM_PRIORITY - 2)
.build()
);
}
public static PWTEventScheduler getScheduler() {
if (instance == null) {
synchronized (PWTEventScheduler.class) {
if (instance == null) {
instance = new PWTEventScheduler();
}
}
}
return instance;
}
public void scheduleTask(Runnable task) {
this.executor.execute(task);
}
}

View File

@@ -144,6 +144,10 @@ public class LinearRegionFile implements IRegionFile {
} }
private synchronized void save() throws IOException { private synchronized void save() throws IOException {
if (MinecraftServer.getServer().hasStopped()) {
// Save only once on shutdown
if (!closed) return;
}
long timestamp = getTimestamp(); long timestamp = getTimestamp();
short chunkCount = 0; short chunkCount = 0;
@@ -217,7 +221,7 @@ public class LinearRegionFile implements IRegionFile {
LOGGER.error("Chunk write IOException {} {}", e, this.path); LOGGER.error("Chunk write IOException {} {}", e, this.path);
} }
if ((System.nanoTime() - this.lastFlushed) >= TimeUnit.NANOSECONDS.toSeconds(RegionFormatConfig.linearFlushFrequency)) { if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - this.lastFlushed) >= (RegionFormatConfig.linearFlushFrequency)) {
this.flushWrapper(); this.flushWrapper();
} }
} }

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Cache player profileResult
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f07870036 100644 index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..50137b67b578b0f9f34bb11a6d572df99ee9fa37 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -70,6 +70,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, @@ -70,6 +70,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
@@ -20,7 +20,7 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
this.server = server; this.server = server;
@@ -303,9 +308,23 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, @@ -303,9 +308,25 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized");
try { try {
@@ -36,7 +36,9 @@ index 9dce1d22c7de3a3dd0e0e8f117cfbb54d1b15042..c4d0372feb6299b785a7e569314cc91f
+ profileResult = ServerLoginPacketListenerImpl.this.server + profileResult = ServerLoginPacketListenerImpl.this.server
+ .getSessionService() + .getSessionService()
+ .hasJoinedServer(string1, string, this.getAddress()); + .hasJoinedServer(string1, string, this.getAddress());
+ playerProfileResultCache.put(string1, profileResult); + if (profileResult != null) {
+ playerProfileResultCache.put(string1, profileResult);
+ }
+ } + }
+ } else { + } else {
+ profileResult = ServerLoginPacketListenerImpl.this.server + profileResult = ServerLoginPacketListenerImpl.this.server

View File

@@ -177,10 +177,10 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6
public boolean visible = true; public boolean visible = true;
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 44d87997e1ce9b846ebed541634a4478334c920c..6b421f84e2d4e286ad4b7624670c90a76e86c8fe 100644 index 44d87997e1ce9b846ebed541634a4478334c920c..87b032ad2ba3e3e0a2e5cfcf185533102247a946 100644
--- a/net/minecraft/server/level/ServerEntity.java --- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java
@@ -460,12 +460,15 @@ public class ServerEntity { @@ -460,15 +460,18 @@ public class ServerEntity {
if (this.entity instanceof LivingEntity) { if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); Set<AttributeInstance> attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync();
if (!attributesToSync.isEmpty()) { if (!attributesToSync.isEmpty()) {
@@ -197,7 +197,11 @@ index 44d87997e1ce9b846ebed541634a4478334c920c..6b421f84e2d4e286ad4b7624670c90a7
+ // Leaf end - petal - Multithreaded tracker - send in main thread + // Leaf end - petal - Multithreaded tracker - send in main thread
} }
attributesToSync.clear(); - attributesToSync.clear();
+ ((LivingEntity)this.entity).getAttributes().getAttributesToSync().clear(); // Leaf - Multithreaded tracker
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce 100644 index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
@@ -234,7 +238,7 @@ index c34cf83f79314198b0f7a747e4ae68b88d09d2cd..c8590517efe4124c2b1db2b927d131b8
if (this.player.isRemoved()) { if (this.player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..d99bbf299af2b2d3a61761c5c3c33c4d371d1b9b 100644 index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..11520972f4fabde3be48edd296351113453b2869 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -26,8 +26,11 @@ public class AttributeInstance { @@ -26,8 +26,11 @@ public class AttributeInstance {
@@ -245,34 +249,86 @@ index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..d99bbf299af2b2d3a61761c5c3c33c4d
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>(); - private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // Leaf start - Multithreaded tracker + // Leaf start - Multithreaded tracker
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled; + private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
+ private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); + private final Map<ResourceLocation, AttributeModifier> modifierById = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); + private final Map<ResourceLocation, AttributeModifier> permanentModifiers = multiThreadedTrackingEnabled ? it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this) : new Object2ObjectArrayMap<>();
+ // Leaf end - Multithreaded tracker + // Leaf end - Multithreaded tracker
private double baseValue; private double baseValue;
private boolean dirty = true; private boolean dirty = true;
private double cachedValue; private double cachedValue;
@@ -116,7 +119,15 @@ public class AttributeInstance {
}
protected void setDirty() {
- this.dirty = true;
+ // Leaf start - Multithreaded tracker
+ if (multiThreadedTrackingEnabled) {
+ synchronized (this) {
+ this.dirty = true;
+ }
+ } else {
+ this.dirty = true;
+ }
+ // Leaf end - Multithreaded tracker
this.onDirty.accept(this);
}
@@ -143,6 +154,17 @@ public class AttributeInstance {
}
public double getValue() {
+ // Leaf start - Multithreaded tracker
+ if (multiThreadedTrackingEnabled) {
+ synchronized (this) {
+ if (this.dirty) {
+ this.cachedValue = this.calculateValue();
+ this.dirty = false;
+ }
+ return this.cachedValue;
+ }
+ }
+ // Leaf end - Multithreaded tracker
if (this.dirty) {
this.cachedValue = this.calculateValue();
this.dirty = false;
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..2b8b335cf5779d1b6eb639935d1b92d82aa85d7f 100644 index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..8a299c81799b3f0c353eecce56afd14b9150df5f 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java --- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -14,11 +14,14 @@ import net.minecraft.nbt.ListTag; @@ -14,11 +14,11 @@ import net.minecraft.nbt.ListTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class AttributeMap { public class AttributeMap {
+ // Leaf start - Multithreaded tracker - // Gale start - Lithium - replace AI attributes with optimized collections
+ private final boolean multiThreadedTrackingEnabled = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled;
// Gale start - Lithium - replace AI attributes with optimized collections
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); - private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); - private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); - private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); - // Gale end - Lithium - replace AI attributes with optimized collections
+ private final Set<AttributeInstance> attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + // Leaf start - Multithreaded tracker
+ private final Set<AttributeInstance> attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); + private final Map<Holder<Attribute>, AttributeInstance> attributes;
// Gale end - Lithium - replace AI attributes with optimized collections + private final Set<AttributeInstance> attributesToSync;
+ private final Set<AttributeInstance> attributesToUpdate;
+ // Leaf end - Multithreaded tracker + // Leaf end - Multithreaded tracker
private final AttributeSupplier supplier; private final AttributeSupplier supplier;
private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
@@ -32,6 +32,17 @@ public class AttributeMap {
// Purpur end - Ridables
this.supplier = defaultAttributes;
this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ this.attributes = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0));
+ this.attributesToSync = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0));
+ this.attributesToUpdate = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0));
+ } else {
+ this.attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
+ this.attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0);
+ this.attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(0);
+ }
+ // Leaf end - Multithreaded tracker
}
private void onAttributeModified(AttributeInstance instance) {
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..d62ff9ebd4b55e1a9a0b51e84be868d844e5a954 100644 index 7bbeed6c998c91e68376d3f17a510d68e3cd0b27..d62ff9ebd4b55e1a9a0b51e84be868d844e5a954 100644
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java --- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java

View File

@@ -1,78 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sun, 16 Feb 2025 19:03:23 +0100
Subject: [PATCH] Optimize AABB
Pretty minor stuff but, it improves AABB.intersect by around ~5%
diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java
index 939fe337c8c1fa52bc0d95cff6d6a735e1125738..1a8432a7ee220ba19327aaad007e9897628bb6cb 100644
--- a/net/minecraft/world/phys/AABB.java
+++ b/net/minecraft/world/phys/AABB.java
@@ -221,13 +221,16 @@ public class AABB {
}
public AABB intersect(AABB other) {
- double max = Math.max(this.minX, other.minX);
- double max1 = Math.max(this.minY, other.minY);
- double max2 = Math.max(this.minZ, other.minZ);
- double min = Math.min(this.maxX, other.maxX);
- double min1 = Math.min(this.maxY, other.maxY);
- double min2 = Math.min(this.maxZ, other.maxZ);
- return new AABB(max, max1, max2, min, min1, min2);
+ // Leaf start - Optimize AABB
+ return new AABB(
+ this.minX > other.minX ? this.minX : other.minX,
+ this.minY > other.minY ? this.minY : other.minY,
+ this.minZ > other.minZ ? this.minZ : other.minZ,
+ this.maxX < other.maxX ? this.maxX : other.maxX,
+ this.maxY < other.maxY ? this.maxY : other.maxY,
+ this.maxZ < other.maxZ ? this.maxZ : other.maxZ
+ );
+ // Leaf end - Optimize AABB
}
public AABB minmax(AABB other) {
@@ -259,16 +262,39 @@ public class AABB {
}
public boolean intersects(AABB other) {
- return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
+ // Leaf start - Optimize AABB
+ // Removed redundant method call overhead
+ return this.minX < other.maxX &&
+ this.maxX > other.minX &&
+ this.minY < other.maxY &&
+ this.maxY > other.minY &&
+ this.minZ < other.maxZ &&
+ this.maxZ > other.minZ;
+ // Leaf end - Optimize AABB
}
public boolean intersects(double x1, double y1, double z1, double x2, double y2, double z2) {
- return this.minX < x2 && this.maxX > x1 && this.minY < y2 && this.maxY > y1 && this.minZ < z2 && this.maxZ > z1;
+ // Leaf start - Optimize AABB
+ // No temporary variables needed, direct comparison
+ return this.minX < x2 &&
+ this.maxX > x1 &&
+ this.minY < y2 &&
+ this.maxY > y1 &&
+ this.minZ < z2 &&
+ this.maxZ > z1;
+ // Leaf end - Optimize AABB
}
public boolean intersects(Vec3 min, Vec3 max) {
return this.intersects(
- Math.min(min.x, max.x), Math.min(min.y, max.y), Math.min(min.z, max.z), Math.max(min.x, max.x), Math.max(min.y, max.y), Math.max(min.z, max.z)
+ // Leaf start - Optimize AABB
+ min.x < max.x ? min.x : max.x,
+ min.y < max.y ? min.y : max.y,
+ min.z < max.z ? min.z : max.z,
+ min.x > max.x ? min.x : max.x,
+ min.y > max.y ? min.y : max.y,
+ min.z > max.z ? min.z : max.z
+ // Leaf end - Optimize AABB
);
}

View File

@@ -1,84 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Tue, 25 Feb 2025 21:13:54 +0100
Subject: [PATCH] Some Optimizations on SerializableChunkData
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 749096358fccbd5d1d13801092255c51096eb001..62a40e88fc03b7f383bd750d72c42747ddd591b4 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -469,14 +469,16 @@ public record SerializableChunkData(
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
} else {
ChunkPos pos = chunk.getPos();
- List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
- LevelChunkSection[] sections = chunk.getSections();
- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
// Paper start - starlight
final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level);
final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level);
final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
+ // Leaf start - Some Optimizations on SerializableChunkData
+ // Pre-allocate with correct capacity to avoid resizing
+ final int expectedSectionCount = maxLightSection - minLightSection + 1;
+ List<SerializableChunkData.SectionData> list = new ArrayList<>(expectedSectionCount);
+ // Leaf end - Some Optimizations on SerializableChunkData
final LevelChunkSection[] chunkSections = chunk.getSections();
final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
@@ -508,10 +510,11 @@ public record SerializableChunkData(
((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
}
- sectionsList.add(sectionData);
+ list.add(sectionData); // Leaf - Some Optimizations on SerializableChunkData
}
// Paper end - starlight
+ // Pre-allocate block entities list with exact size needed
List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
@@ -521,7 +524,16 @@ public record SerializableChunkData(
}
}
- List<CompoundTag> list2 = new ArrayList<>();
+ // Leaf start - Some Optimizations on SerializableChunkData
+ // For entities, use an initial estimated capacity if it's a ProtoChunk
+ int entityEstimate = 64; // Reasonable default size
+ if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
+ ProtoChunk protoChunk = (ProtoChunk)chunk;
+ entityEstimate = Math.max(16, protoChunk.getEntities().size());
+ }
+ List<CompoundTag> list2 = new ArrayList<>(entityEstimate);
+ // Leaf end - Some Optimizations on SerializableChunkData
+
long[] longs = null;
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
ProtoChunk protoChunk = (ProtoChunk)chunk;
@@ -537,14 +549,18 @@ public record SerializableChunkData(
for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) {
long[] rawData = entry.getValue().getRawData();
- map.put(entry.getKey(), (long[])rawData.clone());
+ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); // Leaf - Some Optimizations on SerializableChunkData
}
}
ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(level.getGameTime());
- ShortList[] lists = Arrays.stream(chunk.getPostProcessing())
- .map(list3 -> list3 != null ? new ShortArrayList(list3) : null)
- .toArray(ShortList[]::new);
+ // Leaf start - Some Optimizations on SerializableChunkData
+ ShortList[] postProcessing = chunk.getPostProcessing();
+ ShortList[] lists = new ShortList[postProcessing.length];
+ for (int i = 0; i < postProcessing.length; i++) {
+ lists[i] = postProcessing[i] != null ? new ShortArrayList(postProcessing[i]) : null;
+ }
+ // Leaf end - Some Optimizations on SerializableChunkData
CompoundTag compoundTag = packStructureData(
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
);

View File

@@ -1,133 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Thu, 27 Feb 2025 23:39:32 +0100
Subject: [PATCH] Rework ChunkHolderManager
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..921ac2a1d381268060b9df07c3b2958737e3d14a 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -821,24 +821,21 @@ public final class ChunkHolderManager {
final int sectionShift = ((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift();
- final Predicate<Ticket> expireNow = (final Ticket ticket) -> {
- long removeDelay = ((ChunkSystemTicket<?>)(Object)ticket).moonrise$getRemoveDelay();
- if (removeDelay == NO_TIMEOUT_MARKER) {
- return false;
- }
- --removeDelay;
- ((ChunkSystemTicket<?>)(Object)ticket).moonrise$setRemoveDelay(removeDelay);
- return removeDelay <= 0L;
- };
+ // Leaf start - Rework ChunkHolderManager
+ // Collect sections to process first to avoid concurrent modification issues
+ List<Long> sectionKeys = new ArrayList<>();
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
- final long sectionKey = iterator.nextLong();
+ sectionKeys.add(iterator.nextLong());
+ }
+ for (final Long sectionKey : sectionKeys) {
+ // Skip if section was removed concurrently
if (!this.sectionToChunkToExpireCount.containsKey(sectionKey)) {
- // removed concurrently
continue;
}
+ // Acquire lock for this section only
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
@@ -846,11 +843,15 @@ public final class ChunkHolderManager {
try {
final Long2IntOpenHashMap chunkToExpireCount = this.sectionToChunkToExpireCount.get(sectionKey);
- if (chunkToExpireCount == null) {
- // lost to some race
+ if (chunkToExpireCount == null || chunkToExpireCount.isEmpty()) {
+ // Section was removed or is empty, clean up
+ if (chunkToExpireCount != null && chunkToExpireCount.isEmpty()) {
+ this.sectionToChunkToExpireCount.remove(sectionKey);
+ }
continue;
}
+ // Process each chunk in this section
for (final Iterator<Long2IntMap.Entry> iterator1 = chunkToExpireCount.long2IntEntrySet().fastIterator(); iterator1.hasNext();) {
final Long2IntMap.Entry entry = iterator1.next();
@@ -858,33 +859,50 @@ public final class ChunkHolderManager {
final int expireCount = entry.getIntValue();
final SortedArraySet<Ticket> tickets = this.tickets.get(chunkKey);
+ if (tickets == null || tickets.isEmpty()) {
+ iterator1.remove();
+ continue;
+ }
+
final int levelBefore = getTicketLevelAt(tickets);
+ int expiredCount = 0;
- final int sizeBefore = tickets.size();
- tickets.removeIf(expireNow);
- final int sizeAfter = tickets.size();
- final int levelAfter = getTicketLevelAt(tickets);
+ // More efficient ticket processing - avoids creating a new predicate each time
+ for (Iterator<Ticket> ticketIterator = tickets.iterator(); ticketIterator.hasNext(); ) {
+ Ticket ticket = ticketIterator.next();
+ long removeDelay = ((ChunkSystemTicket<?>) (Object) ticket).moonrise$getRemoveDelay();
+
+ if (removeDelay == NO_TIMEOUT_MARKER) {
+ continue;
+ }
+
+ --removeDelay;
+ if (removeDelay <= 0) {
+ ticketIterator.remove();
+ expiredCount++;
+ } else {
+ ((ChunkSystemTicket<?>) (Object) ticket).moonrise$setRemoveDelay(removeDelay);
+ }
+ }
if (tickets.isEmpty()) {
this.tickets.remove(chunkKey);
}
+ final int levelAfter = getTicketLevelAt(tickets);
if (levelBefore != levelAfter) {
this.updateTicketLevel(chunkKey, levelAfter);
}
- final int newExpireCount = expireCount - (sizeBefore - sizeAfter);
-
- if (newExpireCount == expireCount) {
- continue;
- }
-
- if (newExpireCount != 0) {
- entry.setValue(newExpireCount);
- } else {
+ // Update expire count
+ final int newExpireCount = expireCount - expiredCount;
+ if (newExpireCount <= 0) {
iterator1.remove();
+ } else if (newExpireCount != expireCount) {
+ entry.setValue(newExpireCount);
}
}
+ // Remove empty sections
if (chunkToExpireCount.isEmpty()) {
this.sectionToChunkToExpireCount.remove(sectionKey);
}
@@ -892,6 +910,7 @@ public final class ChunkHolderManager {
this.ticketLockArea.unlock(ticketLock);
}
}
+ // Leaf end - Rework ChunkHolderManager
this.processTicketUpdates();
}

View File

@@ -175,7 +175,7 @@ index 6683df8d0f5a61ab094393f761a3d3a22d6e0455..4fd9313ce2c87383685d80e2533b93d5
// Paper start - rewrite chunk system // Paper start - rewrite chunk system
private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles; private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles;
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6d406207b 100644 index 749096358fccbd5d1d13801092255c51096eb001..ccec1b847ba1a3667206cbeac4ed541a9fb028ea 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -92,6 +92,7 @@ public record SerializableChunkData( @@ -92,6 +92,7 @@ public record SerializableChunkData(
@@ -230,7 +230,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
if (chunkType == ChunkType.LEVELCHUNK) { if (chunkType == ChunkType.LEVELCHUNK) {
return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight
} else { } else {
@@ -570,6 +594,7 @@ public record SerializableChunkData( @@ -554,6 +578,7 @@ public record SerializableChunkData(
persistentDataContainer = chunk.persistentDataContainer.toTagCompound(); persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
} }
// CraftBukkit end // CraftBukkit end
@@ -238,7 +238,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
return new SerializableChunkData( return new SerializableChunkData(
level.registryAccess().lookupOrThrow(Registries.BIOME), level.registryAccess().lookupOrThrow(Registries.BIOME),
pos, pos,
@@ -590,6 +615,7 @@ public record SerializableChunkData( @@ -574,6 +599,7 @@ public record SerializableChunkData(
list1, list1,
compoundTag compoundTag
, persistentDataContainer // CraftBukkit - persistentDataContainer , persistentDataContainer // CraftBukkit - persistentDataContainer
@@ -246,7 +246,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
); );
} }
} }
@@ -674,6 +700,21 @@ public record SerializableChunkData( @@ -658,6 +684,21 @@ public record SerializableChunkData(
compoundTag.put("ChunkBukkitValues", this.persistentDataContainer); compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
} }
// CraftBukkit end // CraftBukkit end
@@ -268,7 +268,7 @@ index 62a40e88fc03b7f383bd750d72c42747ddd591b4..6f4f431d5197e8157908191709776ff6
// Paper start - starlight // Paper start - starlight
if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) { if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) {
// clobber vanilla value to force vanilla to relight // clobber vanilla value to force vanilla to relight
@@ -881,4 +922,50 @@ public record SerializableChunkData( @@ -865,4 +906,50 @@ public record SerializableChunkData(
} }
// Paper end - starlight - convert from record // Paper end - starlight - convert from record
} }

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index d99bbf299af2b2d3a61761c5c3c33c4d371d1b9b..643a2005567e883a2ca545d0f65bc59914ddee00 100644 index 11520972f4fabde3be48edd296351113453b2869..5fe88b105efd3546c675b3397be46bf42e830fb3 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -90,8 +90,13 @@ public class AttributeInstance { @@ -90,8 +90,13 @@ public class AttributeInstance {

View File

@@ -1,183 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sun, 23 Mar 2025 11:51:44 +0100
Subject: [PATCH] Async Block Finding
diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..007da9cb39ff76285c52ce0abdff60997acdff0f 100644
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
@@ -20,6 +20,18 @@ public abstract class MoveToBlockGoal extends Goal {
private final int verticalSearchRange;
protected int verticalSearchStart;
+ // Leaf start - Async Block Finding
+ private static final java.util.concurrent.ExecutorService BLOCK_FINDER_EXECUTOR =
+ java.util.concurrent.Executors.newSingleThreadExecutor(
+ new com.google.common.util.concurrent.ThreadFactoryBuilder()
+ .setNameFormat("Leaf Block Finding Thread - %d")
+ .setDaemon(true)
+ .build());
+
+ private final java.util.concurrent.ConcurrentLinkedQueue<BlockPos> candidateBlocks = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private boolean asyncSearchInProgress = false;
+ // Leaf end - Async Block Finding
+
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) {
this(mob, speedModifier, searchRange, 1);
}
@@ -29,6 +41,10 @@ public abstract class MoveToBlockGoal extends Goal {
super.stop();
this.blockPos = BlockPos.ZERO;
this.mob.movingTarget = null;
+ // Leaf start - Async Block Finding - Reset async state on goal stop
+ this.candidateBlocks.clear();
+ this.asyncSearchInProgress = false;
+ // Leaf end - Async Block Finding - Reset async state on goal stop
}
// Paper end
@@ -53,23 +69,23 @@ public abstract class MoveToBlockGoal extends Goal {
}
protected int nextStartTick(PathfinderMob creature) {
- return reducedTickDelay(200 + creature.getRandom().nextInt(200));
+ return Goal.reducedTickDelay(200 + creature.getRandom().nextInt(200)); // Leaf - Async Block Finding - Use the static method from the Goal class directly
}
@Override
public boolean canContinueToUse() {
- return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.isValidTarget(this.mob.level(), this.blockPos);
+ return this.tryTicks >= -this.maxStayTicks && this.tryTicks <= 1200 && this.blockPos != BlockPos.ZERO && this.isValidTarget(this.mob.level(), this.blockPos); // Leaf - Async Block Finding
}
@Override
public void start() {
- this.moveMobToBlock();
+ if (this.blockPos != BlockPos.ZERO) this.moveMobToBlock(); // Leaf - Async Block Finding
this.tryTicks = 0;
this.maxStayTicks = this.mob.getRandom().nextInt(this.mob.getRandom().nextInt(1200) + 1200) + 1200;
}
protected void moveMobToBlock() {
- this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier);
+ if (this.blockPos != BlockPos.ZERO) this.mob.getNavigation().moveTo(this.blockPos.getX() + 0.5, this.blockPos.getY() + 1, this.blockPos.getZ() + 0.5, this.speedModifier); // Leaf - Async Block Finding
}
public double acceptedDistance() {
@@ -77,7 +93,7 @@ public abstract class MoveToBlockGoal extends Goal {
}
protected BlockPos getMoveToTarget() {
- return this.blockPos.above();
+ return this.blockPos != BlockPos.ZERO ? this.blockPos.above() : BlockPos.ZERO; // Leaf - Async Block Finding
}
@Override
@@ -87,7 +103,10 @@ public abstract class MoveToBlockGoal extends Goal {
@Override
public void tick() {
+ if (this.blockPos == BlockPos.ZERO) return; // Leaf - Async Block Finding
BlockPos moveToTarget = this.getMoveToTarget();
+ if (moveToTarget == BlockPos.ZERO) return; // Leaf - Async Block Finding
+
if (!moveToTarget.closerToCenterThan(this.mob.position(), this.acceptedDistance())) {
this.reachedTarget = false;
this.tryTicks++;
@@ -109,20 +128,90 @@ public abstract class MoveToBlockGoal extends Goal {
}
protected boolean findNearestBlock() {
+ // Leaf start - Async Block Finding
+ if (!org.dreeam.leaf.config.modules.async.AsyncBlockFinding.enabled) {
+ return findNearestBlockSync();
+ }
+
+ while (!candidateBlocks.isEmpty()) {
+ BlockPos pos = candidateBlocks.poll();
+ if (pos != null && this.mob.level().hasChunkAt(pos) &&
+ this.mob.isWithinRestriction(pos) &&
+ this.isValidTarget(this.mob.level(), pos)) {
+
+ this.blockPos = pos;
+ this.mob.movingTarget = pos == BlockPos.ZERO ? null : pos;
+ return true;
+ }
+ }
+
+ if (asyncSearchInProgress) {
+ return false;
+ }
+
+ // Check again before starting, avoids tiny race condition if canUse is called rapidly
+ if (!asyncSearchInProgress) {
+ asyncSearchInProgress = true;
+ final BlockPos centerPos = this.mob.blockPosition().immutable();
+ final int searchRange = this.searchRange;
+ final int verticalRange = this.verticalSearchRange;
+ final int verticalStart = this.verticalSearchStart;
+
+ BLOCK_FINDER_EXECUTOR.execute(() -> {
+ try {
+ generateCandidateBlocks(centerPos, searchRange, verticalRange, verticalStart);
+ } catch (Exception e) {
+ e.printStackTrace(); // Keep basic error logging
+ } finally {
+ asyncSearchInProgress = false;
+ }
+ });
+ }
+
+ return false;
+ }
+
+ private void generateCandidateBlocks(BlockPos center, int searchRange, int verticalRange, int verticalStart) {
+ java.util.List<BlockPos> positions = new java.util.ArrayList<>();
+
+ for (int i2 = verticalStart; i2 <= verticalRange; i2 = i2 > 0 ? -i2 : 1 - i2) {
+ for (int i3 = 0; i3 < searchRange; i3++) {
+ for (int i4 = 0; i4 <= i3; i4 = i4 > 0 ? -i4 : 1 - i4) {
+ for (int i5 = i4 < i3 && i4 > -i3 ? i3 : 0; i5 <= i3; i5 = i5 > 0 ? -i5 : 1 - i5) {
+ BlockPos pos = center.offset(i4, i2 - 1, i5);
+ positions.add(pos.immutable());
+ }
+ }
+ }
+ }
+
+ positions.sort((p1, p2) -> {
+ double d1 = p1.distSqr(center);
+ double d2 = p2.distSqr(center);
+ return Double.compare(d1, d2);
+ });
+
+ for (BlockPos pos : positions) {
+ candidateBlocks.add(pos);
+ }
+ }
+
+ protected boolean findNearestBlockSync() {
+ // Leaf end - Async Block Finding
int i = this.searchRange;
int i1 = this.verticalSearchRange;
- BlockPos blockPos = this.mob.blockPosition();
+ BlockPos blockPosOrigin = this.mob.blockPosition(); // Leaf - Async Block Finding
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (int i2 = this.verticalSearchStart; i2 <= i1; i2 = i2 > 0 ? -i2 : 1 - i2) {
for (int i3 = 0; i3 < i; i3++) {
for (int i4 = 0; i4 <= i3; i4 = i4 > 0 ? -i4 : 1 - i4) {
for (int i5 = i4 < i3 && i4 > -i3 ? i3 : 0; i5 <= i3; i5 = i5 > 0 ? -i5 : 1 - i5) {
- mutableBlockPos.setWithOffset(blockPos, i4, i2 - 1, i5);
+ mutableBlockPos.setWithOffset(blockPosOrigin, i4, i2 - 1, i5); // Leaf - Async Block Finding
if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; // Gale - Airplane - block goal does not load chunks - if this block isn't loaded, continue
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
- this.blockPos = mutableBlockPos;
- this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
+ this.blockPos = mutableBlockPos.immutable(); // Leaf - Async Block Finding
+ this.mob.movingTarget = this.blockPos == BlockPos.ZERO ? null : this.blockPos; // Paper // Leaf - Async Block Finding
return true;
}
}

View File

@@ -0,0 +1,110 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sun, 13 Apr 2025 16:15:17 +0200
Subject: [PATCH] Replace ConcurrentLong2ReferenceChainedHashTable with custom
map
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
index 7eafc5b7cba23d8dec92ecc1050afe3fd8c9e309..9b421f0681fe740520457951b1a1632ada59438a 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java
@@ -16,7 +16,7 @@ public final class ChunkUnloadQueue {
public final int coordinateShift;
private final AtomicLong orderGenerator = new AtomicLong();
- private final ConcurrentLong2ReferenceChainedHashTable<UnloadSection> unloadSections = new ConcurrentLong2ReferenceChainedHashTable<>();
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<UnloadSection> unloadSections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
/*
* Note: write operations do not occur in parallel for any given section.
@@ -32,8 +32,10 @@ public final class ChunkUnloadQueue {
public List<SectionToUnload> retrieveForAllRegions() {
final List<SectionToUnload> ret = new ArrayList<>();
- for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection>> iterator = this.unloadSections.entryIterator(); iterator.hasNext();) {
- final ConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection> entry = iterator.next();
+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
+ for (final Iterator<org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection>> iterator = this.unloadSections.entryIterator(); iterator.hasNext(); ) {
+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<UnloadSection> entry = iterator.next();
+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
final long key = entry.getKey();
final UnloadSection section = entry.getValue();
final int sectionX = CoordinateUtils.getChunkX(key);
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..9f771d78f5401ce8776de38d11e453e8e5857572 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -72,11 +72,13 @@ public final class ChunkHolderManager {
private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE;
public final ReentrantAreaLock ticketLockArea;
- private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>();
- private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>();
+ // Leaf start - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket>> tickets = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>();
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>();
+ // Leaf end - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
final ChunkUnloadQueue unloadQueue;
- private final ConcurrentLong2ReferenceChainedHashTable<NewChunkHolder> chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f);
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<NewChunkHolder> chunkHolders = org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
private final ServerLevel world;
private final ChunkTaskScheduler taskScheduler;
private long currentTick;
@@ -1502,9 +1504,9 @@ public final class ChunkHolderManager {
final JsonArray allTicketsJson = new JsonArray();
ret.add("tickets", allTicketsJson);
- for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>>> iterator = this.tickets.entryIterator();
+ for (final Iterator<org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>>> iterator = this.tickets.entryIterator(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
iterator.hasNext();) {
- final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next();
+ final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
final long coordinate = coordinateTickets.getKey();
final SortedArraySet<Ticket> tickets = coordinateTickets.getValue();
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
index 310a8f80debadd64c2d962ebf83b7d0505ce6e42..3a7fad46465cac8d2c1b0933b457f5b075586709 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
@@ -35,11 +35,11 @@ public abstract class ThreadedTicketLevelPropagator {
}
private final UpdateQueue updateQueue;
- private final ConcurrentLong2ReferenceChainedHashTable<Section> sections;
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<Section> sections; // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
public ThreadedTicketLevelPropagator() {
this.updateQueue = new UpdateQueue();
- this.sections = new ConcurrentLong2ReferenceChainedHashTable<>();
+ this.sections = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
}
// must hold ticket lock for:
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
index e1812910d7c3941dec3d4f1c90f4cf966a631de3..87c229db79f6c6dc98811c7cbccbe5c549fd744f 100644
--- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
+++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
@@ -739,7 +739,7 @@ public final class StarLightInterface {
public static final class ServerLightQueue extends LightQueue {
- private final ConcurrentLong2ReferenceChainedHashTable<ServerChunkTasks> chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>();
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<ServerChunkTasks> chunkTasks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
public ServerLightQueue(final StarLightInterface lightInterface) {
super(lightInterface);
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 0f9d18dd29e210ad656da211a3cb1cb25cd4efb1..f89fb321e50338e7765476cb5d7bdf2f02a497b3 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -76,7 +76,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@VisibleForDebug
private NaturalSpawner.SpawnState lastSpawnState;
// Paper start
- private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
+ private final org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new org.dreeam.leaf.util.map.spottedleaf.LeafConcurrentLong2ReferenceChainedHashTable<>(); // Leaf - Replace ConcurrentLong2ReferenceChainedHashTable with custom map
public int getFullChunksCount() {
return this.fullChunks.size();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Mon, 14 Apr 2025 14:36:57 +0200
Subject: [PATCH] Optimize ThreadedTicketLevelPropagator
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
index 3a7fad46465cac8d2c1b0933b457f5b075586709..a2d76e6fabf2749a1a9f21fe6bdf6524af8bb9b7 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java
@@ -998,6 +998,7 @@ public abstract class ThreadedTicketLevelPropagator {
final int decodeOffsetZ = -this.encodeOffsetZ;
final int encodeOffset = this.coordinateOffset;
final int sectionOffset = this.sectionIndexOffset;
+ final Section[] sectionsArray = this.sections; // Leaf - Optimize ThreadedTicketLevelPropagator
final Long2ByteLinkedOpenHashMap updatedPositions = this.updatedPositions;
@@ -1012,13 +1013,27 @@ public abstract class ThreadedTicketLevelPropagator {
int propagateDirectionBitset = (int)(queueValue >>> (COORDINATE_BITS + COORDINATE_BITS + LEVEL_BITS)) & ((1 << 16) - 1);
if ((queueValue & FLAG_RECHECK_LEVEL) != 0L) {
- if (this.getLevel(posX, posZ) != propagatedLevel) {
+ // Leaf start - Optimize ThreadedTicketLevelPropagator
+ final int sectionX = posX >> SECTION_SHIFT;
+ final int sectionZ = posZ >> SECTION_SHIFT;
+ final Section section = sectionsArray[sectionX + (sectionZ * SECTION_CACHE_WIDTH) + sectionOffset];
+ final int localIdx = (posX & (SECTION_SIZE - 1)) | ((posZ & (SECTION_SIZE - 1)) << SECTION_SHIFT);
+ if ((section.levels[localIdx] & 0xFF) != propagatedLevel) {
+ // Leaf end - Optimize ThreadedTicketLevelPropagator
// not at the level we expect, so something changed.
continue;
}
} else if ((queueValue & FLAG_WRITE_LEVEL) != 0L) {
// these are used to restore sources after a propagation decrease
- this.setLevel(posX, posZ, propagatedLevel);
+ // Leaf start - Optimize ThreadedTicketLevelPropagator
+ final int sectionX = posX >> SECTION_SHIFT;
+ final int sectionZ = posZ >> SECTION_SHIFT;
+ final Section section = sectionsArray[sectionX + (sectionZ * SECTION_CACHE_WIDTH) + sectionOffset];
+ final int localIdx = (posX & (SECTION_SIZE - 1)) | ((posZ & (SECTION_SIZE - 1)) << SECTION_SHIFT);
+ final short currentLevel = section.levels[localIdx];
+ section.levels[localIdx] = (short) ((currentLevel & ~0xFF) | (propagatedLevel & 0xFF));
+ updatedPositions.put(CoordinateUtils.getChunkKey(posX, posZ), (byte) propagatedLevel);
+ // Leaf end - Optimize ThreadedTicketLevelPropagator
}
// this bitset represents the values that we have not propagated to
@@ -1093,7 +1108,7 @@ public abstract class ThreadedTicketLevelPropagator {
currentPropagation ^= (bitsetLine1 | bitsetLine2 | bitsetLine3);
// now try to propagate
- final Section section = this.sections[sectionIndex];
+ final Section section = sectionsArray[sectionIndex]; // Leaf - Optimize ThreadedTicketLevelPropagator
// lower 8 bits are current level, next upper 7 bits are source level, next 1 bit is updated source flag
final short currentStoredLevel = section.levels[localIndex];
@@ -1144,6 +1159,7 @@ public abstract class ThreadedTicketLevelPropagator {
final int decodeOffsetZ = -this.encodeOffsetZ;
final int encodeOffset = this.coordinateOffset;
final int sectionOffset = this.sectionIndexOffset;
+ final Section[] sectionsArray = this.sections; // Leaf - Optimize ThreadedTicketLevelPropagator
final Long2ByteLinkedOpenHashMap updatedPositions = this.updatedPositions;
@@ -1227,7 +1243,7 @@ public abstract class ThreadedTicketLevelPropagator {
final long bitsetLine3 = currentPropagation & (7L << (start + (8 + 8)));
// now try to propagate
- final Section section = this.sections[sectionIndex];
+ final Section section = sectionsArray[sectionIndex]; // Leaf - Optimize ThreadedTicketLevelPropagator
// lower 8 bits are current level, next upper 7 bits are source level, next 1 bit is updated source flag
final short currentStoredLevel = section.levels[localIndex];

View File

@@ -1,319 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sat, 29 Mar 2025 13:40:46 +0100
Subject: [PATCH] Async Target Finding
diff --git a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
index 024792900c8ab716e91ef512d2da22548075044d..7f256793232cfa9666728223cb9964e49ff8b6ba 100644
--- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
+++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
@@ -16,9 +16,37 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
protected final Class<T> targetType;
protected final int randomInterval;
@Nullable
- protected LivingEntity target;
+ protected volatile LivingEntity target; // Leaf - Async Target Finding
protected TargetingConditions targetConditions;
+ // Leaf start - Async Target Finding
+ // Single thread executor to prevent overwhelming the server
+ private static final java.util.concurrent.ExecutorService TARGET_FINDER_EXECUTOR = java.util.concurrent.Executors.newSingleThreadExecutor(r -> {
+ Thread thread = new Thread(r, "Leaf - Target-Finder-Thread");
+ thread.setDaemon(true);
+ thread.setPriority(Thread.MIN_PRIORITY); // Lower priority to avoid competing with main thread
+ return thread;
+ });
+
+ // Flag to track if a search is in progress
+ private final java.util.concurrent.atomic.AtomicBoolean isSearching = new java.util.concurrent.atomic.AtomicBoolean(false);
+ private final java.util.concurrent.atomic.AtomicReference<LivingEntity> pendingTarget = new java.util.concurrent.atomic.AtomicReference<>(null);
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ TARGET_FINDER_EXECUTOR.shutdown();
+ TARGET_FINDER_EXECUTOR.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } finally {
+ if (!TARGET_FINDER_EXECUTOR.isTerminated()) {
+ TARGET_FINDER_EXECUTOR.shutdownNow();
+ }
+ }
+ }));
+ }
+ // Leaf end - Async Target Finding
+
public NearestAttackableTargetGoal(Mob mob, Class<T> targetType, boolean mustSee) {
this(mob, targetType, 10, mustSee, false, null);
}
@@ -46,8 +74,14 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
if (this.randomInterval > 0 && this.mob.getRandom().nextInt(this.randomInterval) != 0) {
return false;
} else {
- this.findTarget();
- return this.target != null;
+ // Leaf start - Async Target Finding
+ findTarget();
+ LivingEntity pending = pendingTarget.getAndSet(null);
+ if (pending != null && !pending.isRemoved() && pending.isAlive()) {
+ this.target = pending;
+ }
+ return this.target != null && this.target.isAlive() && !this.target.isRemoved();
+ // Leaf end - Async Target Finding
}
}
@@ -55,25 +89,239 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
return this.mob.getBoundingBox().inflate(targetDistance, targetDistance, targetDistance);
}
+ // Leaf start - Async Target Finding
+ // Async find target implementation with safer entity handling
protected void findTarget() {
- ServerLevel serverLevel = getServerLevel(this.mob);
- if (this.targetType != Player.class && this.targetType != ServerPlayer.class) {
- this.target = serverLevel.getNearestEntity(
- this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true),
- this.getTargetConditions(),
- this.mob,
- this.mob.getX(),
- this.mob.getEyeY(),
- this.mob.getZ()
- );
- } else {
- this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
+ // If async is disabled or we're already searching, use sync method
+ if (!org.dreeam.leaf.config.modules.async.AsyncTargetFinding.enabled || !isSearching.compareAndSet(false, true)) {
+ findTargetSync();
+ return;
}
+
+ // Capture mutable state to avoid race conditions
+ final Mob mob = this.mob;
+
+ // Safety check
+ if (mob == null || mob.isRemoved() || !mob.isAlive()) {
+ isSearching.set(false);
+ return;
+ }
+
+ final double x = mob.getX();
+ final double y = mob.getEyeY();
+ final double z = mob.getZ();
+ final double followDistance = this.getFollowDistance();
+ final TargetingConditions targetConditions = this.getTargetConditions();
+ final Class<T> targetType = this.targetType;
+
+ // Start async search with immutable captured state - using submit instead of runAsync
+ java.util.concurrent.CompletableFuture.supplyAsync(() -> {
+ try {
+ ServerLevel serverLevel = getServerLevel(mob);
+ if (serverLevel == null) {
+ return null;
+ }
+ if (mob.isRemoved() || !mob.isAlive()) {
+ return null;
+ }
+
+ try {
+ if (targetType != Player.class && targetType != ServerPlayer.class) {
+ AABB searchArea = new AABB(
+ x - followDistance, y - followDistance, z - followDistance,
+ x + followDistance, y + followDistance, z + followDistance
+ );
+
+ java.util.List<T> entities = null;
+ try {
+ entities = mob.level().getEntitiesOfClass(targetType, searchArea, entity -> true);
+ } catch (Exception e) {
+ System.err.println("Error getting entities: " + e.getMessage());
+ return null;
+ }
+
+ if (entities != null && !entities.isEmpty()) {
+ return findNearestEntitySafely(entities, targetConditions, mob, x, y, z, serverLevel);
+ }
+ } else {
+ return findNearestPlayerSafely(targetConditions, mob, x, y, z, serverLevel);
+ }
+ } catch (Exception e) {
+ System.err.println("Error finding entities in async target finder: " + e.getMessage());
+ }
+
+ return null;
+ } catch (Exception e) {
+ System.err.println("Error during async target finding: " + e.getMessage());
+ return null;
+ } finally {
+ isSearching.set(false);
+ }
+ }, TARGET_FINDER_EXECUTOR).thenAccept(result -> {
+ if (result != null && result.isAlive() && !result.isRemoved()) {
+ pendingTarget.set(result);
+ }
+ });
}
+ @Nullable
+ private LivingEntity findNearestEntitySafely(
+ java.util.List<? extends LivingEntity> entities,
+ TargetingConditions conditions,
+ Mob source,
+ double x,
+ double y,
+ double z,
+ ServerLevel level) {
+
+ if (entities == null || entities.isEmpty() || level == null) {
+ return null;
+ }
+
+ try {
+ double closestDistSq = -1.0;
+ LivingEntity closest = null;
+
+ for (int i = 0; i < entities.size(); i++) {
+ try {
+ LivingEntity entity = entities.get(i);
+ if (entity == null || entity.isRemoved() || !entity.isAlive()) {
+ continue;
+ }
+
+ if (conditions.test(level, source, entity)) {
+ double dx = entity.getX() - x;
+ double dy = entity.getY() - y;
+ double dz = entity.getZ() - z;
+ double distSq = dx * dx + dy * dy + dz * dz;
+
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
+ closestDistSq = distSq;
+ closest = entity;
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ break;
+ } catch (Exception e) {
+ System.err.println("Error processing entity in findNearestEntitySafely: " + e.getMessage());
+ continue;
+ }
+ }
+
+ return closest;
+ } catch (Exception e) {
+ System.err.println("Error in findNearestEntitySafely: " + e.getMessage());
+ return null;
+ }
+ }
+
+ @Nullable
+ private Player findNearestPlayerSafely(
+ TargetingConditions conditions,
+ Mob source,
+ double x,
+ double y,
+ double z,
+ ServerLevel level) {
+
+ if (level == null) {
+ return null;
+ }
+
+ try {
+ java.util.List<? extends Player> players = level.players();
+ if (players == null || players.isEmpty()) {
+ return null;
+ }
+
+ double closestDistSq = -1.0;
+ Player closest = null;
+
+ for (int i = 0; i < players.size(); i++) {
+ try {
+ Player player = players.get(i);
+ if (player == null || player.isRemoved() || !player.isAlive()) {
+ continue;
+ }
+
+ if (conditions.test(level, source, player)) {
+ double dx = player.getX() - x;
+ double dy = player.getY() - y;
+ double dz = player.getZ() - z;
+ double distSq = dx * dx + dy * dy + dz * dz;
+
+ if (closestDistSq == -1.0 || distSq < closestDistSq) {
+ closestDistSq = distSq;
+ closest = player;
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ break;
+ } catch (Exception e) {
+ System.err.println("Error processing player in findNearestPlayerSafely: " + e.getMessage());
+ continue;
+ }
+ }
+
+ return closest;
+ } catch (Exception e) {
+ System.err.println("Error in findNearestPlayerSafely: " + e.getMessage());
+ return null;
+ }
+ }
+
+ // Synchronous fallback method
+ private void findTargetSync() {
+ try {
+ ServerLevel serverLevel = getServerLevel(this.mob);
+ if (serverLevel == null) {
+ return;
+ }
+
+ if (this.targetType != Player.class && this.targetType != ServerPlayer.class) {
+ try {
+ this.target = serverLevel.getNearestEntity(
+ this.mob.level().getEntitiesOfClass(this.targetType, this.getTargetSearchArea(this.getFollowDistance()), entity -> true),
+ this.getTargetConditions(),
+ this.mob,
+ this.mob.getX(),
+ this.mob.getEyeY(),
+ this.mob.getZ()
+ );
+ } catch (Exception e) {
+ System.err.println("Error in sync entity finding: " + e.getMessage());
+ this.target = null;
+ }
+ } else {
+ try {
+ this.target = serverLevel.getNearestPlayer(this.getTargetConditions(), this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ());
+ } catch (Exception e) {
+ System.err.println("Error in sync player finding: " + e.getMessage());
+ this.target = null;
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("Error in findTargetSync: " + e.getMessage());
+ this.target = null;
+ }
+ }
+ // Leaf end - Async Target Finding
+
@Override
public void start() {
- this.mob.setTarget(this.target, this.target instanceof ServerPlayer ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); // CraftBukkit - reason
+ // Leaf start - Async Target Finding
+ LivingEntity targetEntity = this.target;
+ if (targetEntity != null && !targetEntity.isRemoved() && targetEntity.isAlive()) {
+ try {
+ this.mob.setTarget(targetEntity, targetEntity instanceof ServerPlayer ?
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER :
+ org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY);
+ } catch (Exception e) {
+ System.err.println("Error in setTarget: " + e.getMessage());
+ this.target = null;
+ }
+ }
+ // Leaf end - Async Target Finding
super.start();
}

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Mon, 14 Apr 2025 18:07:21 +0200
Subject: [PATCH] Optimise MobEffectUtil#getDigSpeedAmplification
diff --git a/net/minecraft/world/effect/MobEffectUtil.java b/net/minecraft/world/effect/MobEffectUtil.java
index 93a07a96f74e3ba73986324b39923c6a2802f8ee..e900c91c8eb36029726f7833df1d9be4030b3ad8 100644
--- a/net/minecraft/world/effect/MobEffectUtil.java
+++ b/net/minecraft/world/effect/MobEffectUtil.java
@@ -27,17 +27,21 @@ public final class MobEffectUtil {
}
public static int getDigSpeedAmplification(LivingEntity entity) {
- int i = 0;
- int i1 = 0;
- if (entity.hasEffect(MobEffects.HASTE)) {
- i = entity.getEffect(MobEffects.HASTE).getAmplifier();
+ // Leaf start - Optimise MobEffectUtil#getDigSpeedAmplification
+ int digAmplifier = 0;
+ int conduitAmplifier = 0;
+ MobEffectInstance digEffect = entity.getEffect(MobEffects.HASTE);
+ if (digEffect != null) {
+ digAmplifier = digEffect.getAmplifier();
}
- if (entity.hasEffect(MobEffects.CONDUIT_POWER)) {
- i1 = entity.getEffect(MobEffects.CONDUIT_POWER).getAmplifier();
+ MobEffectInstance conduitEffect = entity.getEffect(MobEffects.CONDUIT_POWER);
+ if (conduitEffect != null) {
+ conduitAmplifier = conduitEffect.getAmplifier();
}
- return Math.max(i, i1);
+ return Math.max(digAmplifier, conduitAmplifier);
+ // Leaf end - Optimise MobEffectUtil#getDigSpeedAmplification
}
public static boolean hasWaterBreathing(LivingEntity entity) {

View File

@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com> From: Taiyou06 <kaandindar21@gmail.com>
Date: Fri, 28 Feb 2025 01:35:49 +0100 Date: Mon, 14 Apr 2025 20:07:52 +0200
Subject: [PATCH] Optimize chunkUnload Subject: [PATCH] Optimise chunkUnloads
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java
@@ -211,3 +211,131 @@ index 36c033b0ee63dfc273d721fb4b614733e8fdef19..dc9f1bc6dd8b1057da3416e24f15f232
return new LevelChunkSection(this); return new LevelChunkSection(this);
} }
} }
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index ccec1b847ba1a3667206cbeac4ed541a9fb028ea..5c2d29e749fdd317f231489401601c82191552bf 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -493,14 +493,16 @@ public record SerializableChunkData(
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
} else {
ChunkPos pos = chunk.getPos();
- List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
- LevelChunkSection[] sections = chunk.getSections();
- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
// Paper start - starlight
final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level);
final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level);
final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
+ // Leaf start - Optimize chunkUnload
+ // Pre-allocate with correct capacity to avoid resizing
+ final int expectedSectionCount = maxLightSection - minLightSection + 1;
+ List<SerializableChunkData.SectionData> list = new ArrayList<>(expectedSectionCount);
+ // Leaf end - Optimize chunkUnload
final LevelChunkSection[] chunkSections = chunk.getSections();
final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
@@ -518,10 +520,20 @@ public record SerializableChunkData(
continue;
}
+ // Leaf start - Optimize chunkUnload
+ DataLayer blockDataLayer = null;
+ if (blockNibble != null && blockNibble.data != null) {
+ blockDataLayer = new DataLayer(blockNibble.data);
+ }
+
+ DataLayer skyDataLayer = null;
+ if (skyNibble != null && skyNibble.data != null) {
+ skyDataLayer = new DataLayer(skyNibble.data);
+ }
+ // Leaf end - Optimize chunkUnload
+
final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
- lightSection, chunkSection,
- blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
- skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
+ lightSection, chunkSection, blockDataLayer, skyDataLayer // Leaf - Optimize chunkUnload
);
if (blockNibble != null) {
@@ -532,12 +544,16 @@ public record SerializableChunkData(
((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
}
- sectionsList.add(sectionData);
+ list.add(sectionData); // Leaf - Optimize chunkUnload
}
// Paper end - starlight
- List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
+ // Leaf start - Optimize chunkUnload
+ // Pre-allocate block entities list with exact size needed
+ final int blockEntityCount = chunk.getBlockEntitiesPos().size();
+ List<CompoundTag> list1 = blockEntityCount > 0 ? new ArrayList<>(blockEntityCount) : java.util.Collections.emptyList();
+ if (blockEntityCount > 0) // Leaf - Optimize chunkUnload
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
CompoundTag blockEntityNbtForSaving = chunk.getBlockEntityNbtForSaving(blockPos, level.registryAccess());
if (blockEntityNbtForSaving != null) {
@@ -545,15 +561,27 @@ public record SerializableChunkData(
}
}
- List<CompoundTag> list2 = new ArrayList<>();
+ // Leaf start - Optimize chunkUnload
+ // For entities, use an initial estimated capacity if it's a ProtoChunk
+ List<CompoundTag> list2;
long[] longs = null;
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
ProtoChunk protoChunk = (ProtoChunk)chunk;
- list2.addAll(protoChunk.getEntities());
+ // Leaf start - Optimize chunkUnload
+ int entitySize = protoChunk.getEntities().size();
+ if (entitySize > 0) {
+ list2 = new ArrayList<>(Math.max(16, entitySize));
+ list2.addAll(protoChunk.getEntities());
+ } else {
+ list2 = java.util.Collections.emptyList();
+ }
+ // Leaf end - Optimize chunkUnload
CarvingMask carvingMask = protoChunk.getCarvingMask();
if (carvingMask != null) {
longs = carvingMask.toArray();
}
+ } else {
+ list2 = java.util.Collections.emptyList(); // Leaf - Optimize chunkUnload
}
Map<Heightmap.Types, long[]> map = new EnumMap<>(Heightmap.Types.class);
@@ -561,14 +589,26 @@ public record SerializableChunkData(
for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) {
long[] rawData = entry.getValue().getRawData();
- map.put(entry.getKey(), (long[])rawData.clone());
+ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); // Leaf - Optimize chunkUnload
}
}
ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(level.getGameTime());
- ShortList[] lists = Arrays.stream(chunk.getPostProcessing())
- .map(list3 -> list3 != null ? new ShortArrayList(list3) : null)
- .toArray(ShortList[]::new);
+ // Leaf start - Optimize chunkUnload - remove stream
+ ShortList[] postProcessing = chunk.getPostProcessing();
+ ShortList[] lists = new ShortList[postProcessing.length];
+ for (int i = 0; i < postProcessing.length; i++) {
+ ShortList source = postProcessing[i];
+ // Only create a new list if there's actual data to copy
+ if (source != null) {
+ int size = source.size();
+ if (size > 0) {
+ lists[i] = new ShortArrayList(size);
+ lists[i].addAll(source);
+ }
+ }
+ }
+ // Leaf end - Optimize chunkUnload - remove stream
CompoundTag compoundTag = packStructureData(
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
);

View File

@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <mc@jvavav.com>
Date: Sat, 19 Apr 2025 22:12:19 +0800
Subject: [PATCH] Optimize BlockEntityType#isValid
diff --git a/net/minecraft/world/level/block/entity/BlockEntityType.java b/net/minecraft/world/level/block/entity/BlockEntityType.java
index 386e6a48701b4c9256e33174123381a93d61e292..2bc620ceb8368e9a74b831de698de94ac9c47c3b 100644
--- a/net/minecraft/world/level/block/entity/BlockEntityType.java
+++ b/net/minecraft/world/level/block/entity/BlockEntityType.java
@@ -256,6 +256,14 @@ public class BlockEntityType<T extends BlockEntity> {
private BlockEntityType(BlockEntityType.BlockEntitySupplier<? extends T> factory, Set<Block> validBlocks) {
this.factory = factory;
this.validBlocks = validBlocks;
+ // Leaf start - Optimize BlockEntityType#isValid
+ for (Block validBlock : validBlocks) {
+ if (validBlock.blockEntityType != null) {
+ throw new IllegalStateException("Duplicate block entity type");
+ }
+ validBlock.blockEntityType = this;
+ }
+ // Leaf end - Optimize BlockEntityType#isValid
}
public T create(BlockPos pos, BlockState state) {
@@ -263,7 +271,7 @@ public class BlockEntityType<T extends BlockEntity> {
}
public boolean isValid(BlockState state) {
- return this.validBlocks.contains(state.getBlock());
+ return state.getBlock().blockEntityType == this; // Leaf - Optimize BlockEntityType#isValid - remove hash lookup
}
@Deprecated
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index 3219b9caa654c7a64e5e4a92178528e1104b34c7..6d522e9485fadd6fc0f350cb30ba5224aa046d4f 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -101,6 +101,7 @@ public abstract class BlockBehaviour implements FeatureElement {
protected final BlockBehaviour.Properties properties;
protected final Optional<ResourceKey<LootTable>> drops;
protected final String descriptionId;
+ @Nullable public net.minecraft.world.level.block.entity.BlockEntityType blockEntityType = null; // Leaf - Optimize BlockEntityType#isValid
public BlockBehaviour(BlockBehaviour.Properties properties) {
this.hasCollision = properties.hasCollision;

View File

@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:32:32 -0700
Subject: [PATCH] PaperPR: Add ticket on player join to avoid chunk
load-unload-load cycle
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/11398
Adding the entity will add and then immediately remove an entity load ticket, which would result in the chunk loading and then unloading before being loaded again once the player chunk loader reacts (delay can vary based on rate limit configs)
By adding a ticket with a short removal delay we attempt to keep the chunk loaded until the player chunk loader reacts, but this is not a guarantee due to the aforementioned rate limit configs. Plugins should still handle load/unload events as normal, however this will reduce redundant calls.
The delay is currently set to 2 seconds, however, we may want to adjust this before merging (for example the player chunk unload delay is 5 seconds)
fixes Paper#9581
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index 67275e803e0287306b163f4eec17388b9c701a8c..4344e5d9eda940849352e08734f95de2036bd87a 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -48,6 +48,7 @@ public final class RegionizedPlayerChunkLoader {
public static final TicketType PLAYER_TICKET = ChunkSystemTicketType.create("chunk_system:player_ticket", Long::compareTo);
public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5L * 20L);
+ public static final TicketType PLAYER_JOIN = ChunkSystemTicketType.create("chunk_system:player_join", (a, b) -> 0, 5 * 20); // Paper - Add ticket on player join to avoid chunk load-unload-load cycle
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 07dafa0e12fd51e4b9e968e22b20f000d04f6dbc..8173242d149709f092e6d609f6e1d831eca0a884 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -330,6 +330,13 @@ public abstract class PlayerList {
// this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
// Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
player.supressTrackerForLogin = true;
+ // Paper start - Add ticket on player join to avoid chunk load-unload-load cycle
+ serverLevel.moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PLAYER_JOIN,
+ player.chunkPosition(),
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.TICK_TICKET_LEVEL,
+ net.minecraft.util.Unit.INSTANCE);
+ // Paper end - Add ticket on player join to avoid chunk load-unload-load cycle
serverLevel.addNewPlayer(player);
this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player);
// Paper end - Fire PlayerJoinEvent when Player is actually ready

View File

@@ -0,0 +1,199 @@
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
- Secret patches (WIP)
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3..f998cf8d70302a21289de4d84b46d322d0b8a8fe 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -617,13 +617,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
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 <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> 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<PacketListener> 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<PacketListener> getCurrentPacketProcessors() {
- java.util.List<PacketListener> 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 f89fb321e50338e7765476cb5d7bdf2f02a497b3..83fcef85ed2a5410275e4419e4356994016f21d1 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 8ade900d016026cde482ccbca7a411993d9eadd9..414a70c352263a1cd2bfb938053990ea2be2b2c3 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1367,13 +1367,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
@@ -1386,13 +1380,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 960ae487a5a88a5fe9899c16b9553cea0fbfba37..1ff8522b81eb4bc6297b44a1f2a48c8021104185 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1138,16 +1138,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)) {
@@ -1155,16 +1145,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 {
@@ -1298,13 +1279,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) {
@@ -4771,9 +4745,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) {
@@ -4881,9 +4853,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);

View File

@@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Thu, 1 May 2025 22:28:50 +0200
Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
index 7b686d834e4eb36be5758b0e0a846a70d1e2294b..37930d1b87378ac3e8c7f5ebd79148bb66771f48 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -382,6 +382,13 @@ public final class ChunkEntitySlices {
private E[] storage;
private int size;
+ // Sakura start - use methods from EntityList
+ private it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap entityToIndex = null;
+ private void setupIndexMap() {
+ this.entityToIndex = new it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap(2, 0.8f);
+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
+ }
+ // Sakura end - use methods from EntityList
public BasicEntityList() {
this(0);
@@ -402,6 +409,7 @@ public final class ChunkEntitySlices {
private void resize() {
if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations
this.storage = (E[])new Entity[DEFAULT_CAPACITY];
+ this.setupIndexMap(); // Sakura - use methods from EntityList
} else {
this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
}
@@ -415,6 +423,7 @@ public final class ChunkEntitySlices {
} else {
this.storage[idx] = entity;
}
+ this.entityToIndex.put(entity.getId(), idx); // Sakura - use methods from EntityList
}
public int indexOf(final E entity) {
@@ -430,24 +439,32 @@ public final class ChunkEntitySlices {
}
public boolean remove(final E entity) {
- final int idx = this.indexOf(entity);
- if (idx == -1) {
+ // Sakura start - use methods from EntityList
+ if (this.entityToIndex == null) {
return false;
}
- final int size = --this.size;
- final E[] storage = this.storage;
- if (idx != size) {
- System.arraycopy(storage, idx + 1, storage, idx, size - idx);
+ final int index = this.entityToIndex.remove(entity.getId());
+ if (index == Integer.MIN_VALUE) {
+ return false;
}
- storage[size] = null;
+ // move the entity at the end to this index
+ final int endIndex = --this.size;
+ final E end = this.storage[endIndex];
+ if (index != endIndex) {
+ // not empty after this call
+ this.entityToIndex.put(end.getId(), index); // update index
+ }
+ this.storage[index] = end;
+ this.storage[endIndex] = null;
+ // Sakura end - use methods from EntityList
return true;
}
public boolean has(final E entity) {
- return this.indexOf(entity) != -1;
+ return this.entityToIndex != null && this.entityToIndex.containsKey(entity.getId()); // Sakura - use methods from EntityList
}
}

View File

@@ -23,7 +23,7 @@ and, in my opinion, worth the low risk of minor mob-spawning-related
inconsistencies. inconsistencies.
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a475ad729d 100644 index ece6db7b9a0dfd535141c0c756947c4898140503..13df153d7b63a5c36a2fd14d3292e7eb32a394df 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java --- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java +++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
@@ -11,7 +11,7 @@ public final class IteratorSafeOrderedReferenceSet<E> { @@ -11,7 +11,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
@@ -31,7 +31,7 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
private final Reference2IntLinkedOpenHashMap<E> indexMap; private final Reference2IntLinkedOpenHashMap<E> indexMap;
- private int firstInvalidIndex = -1; - private int firstInvalidIndex = -1;
+ private volatile int firstInvalidIndex = -1; // Leaf - Async mob spawning - volatile + private final java.util.concurrent.atomic.AtomicInteger firstInvalidIndex = new java.util.concurrent.atomic.AtomicInteger(-1); // Leaf - Pufferfish - Async mob spawning - atomic
/* list impl */ /* list impl */
private E[] listElements; private E[] listElements;
@@ -44,7 +44,7 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
public IteratorSafeOrderedReferenceSet() { public IteratorSafeOrderedReferenceSet() {
this(Object.class); this(Object.class);
@@ -99,7 +99,7 @@ public final class IteratorSafeOrderedReferenceSet<E> { @@ -99,11 +99,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
} }
public int createRawIterator() { public int createRawIterator() {
@@ -53,6 +53,11 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
if (this.indexMap.isEmpty()) { if (this.indexMap.isEmpty()) {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} else { } else {
- return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0;
+ return this.firstInvalidIndex.get() == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0; // Leaf - Pufferfish - Async mob spawning
}
}
@@ -120,7 +120,7 @@ public final class IteratorSafeOrderedReferenceSet<E> { @@ -120,7 +120,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
} }
@@ -62,7 +67,19 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
if (this.getFragFactor() >= this.maxFragFactor) { if (this.getFragFactor() >= this.maxFragFactor) {
this.defrag(); this.defrag();
} }
@@ -137,7 +137,7 @@ public final class IteratorSafeOrderedReferenceSet<E> { @@ -130,14 +130,17 @@ public final class IteratorSafeOrderedReferenceSet<E> {
public boolean remove(final E element) {
final int index = this.indexMap.removeInt(element);
if (index >= 0) {
- if (this.firstInvalidIndex < 0 || index < this.firstInvalidIndex) {
- this.firstInvalidIndex = index;
+ // Leaf start - Pufferfish - Async mob spawning
+ int firstInvalidIndex = this.firstInvalidIndex.get();
+ if (firstInvalidIndex < 0 || index < firstInvalidIndex) {
+ this.firstInvalidIndex.set(index);
}
+ // Leaf end - Pufferfish - Async mob spawning
if (this.listElements[index] != element) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
this.listElements[index] = null; this.listElements[index] = null;
@@ -71,7 +88,49 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
this.defrag(); this.defrag();
} }
//this.check(); //this.check();
@@ -235,7 +235,7 @@ public final class IteratorSafeOrderedReferenceSet<E> { @@ -169,14 +172,16 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
private void defrag() {
- if (this.firstInvalidIndex < 0) {
+ // Leaf start - Pufferfish - Async mob spawning
+ int firstInvalidIndex = this.firstInvalidIndex.get();
+ if (firstInvalidIndex < 0) {
return; // nothing to do
}
if (this.indexMap.isEmpty()) {
Arrays.fill(this.listElements, 0, this.listSize, null);
this.listSize = 0;
- this.firstInvalidIndex = -1;
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
//this.check();
return;
}
@@ -186,11 +191,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
int lastValidIndex;
java.util.Iterator<Reference2IntMap.Entry<E>> iterator;
- if (this.firstInvalidIndex == 0) {
+ if (firstInvalidIndex == 0) { // Leaf - Pufferfish - Async mob spawning
iterator = this.indexMap.reference2IntEntrySet().fastIterator();
lastValidIndex = 0;
} else {
- lastValidIndex = this.firstInvalidIndex;
+ lastValidIndex = firstInvalidIndex; // Leaf - Pufferfish - Async mob spawning
final E key = backingArray[lastValidIndex - 1];
iterator = this.indexMap.reference2IntEntrySet().fastIterator(new Reference2IntMap.Entry<E>() {
@Override
@@ -221,7 +226,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
// cleanup end
Arrays.fill(backingArray, lastValidIndex, this.listSize, null);
this.listSize = lastValidIndex;
- this.firstInvalidIndex = -1;
+ this.firstInvalidIndex.set(-1); // Leaf - Pufferfish - Async mob spawning
//this.check();
}
@@ -235,7 +240,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
} }
public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) { public IteratorSafeOrderedReferenceSet.Iterator<E> iterator(final int flags) {
@@ -80,3 +139,12 @@ index ece6db7b9a0dfd535141c0c756947c4898140503..82738ad42853e328532d854e3369e6a4
return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize); return new BaseIterator<>(this, true, (flags & ITERATOR_FLAG_SEE_ADDITIONS) != 0 ? Integer.MAX_VALUE : this.listSize);
} }
@@ -322,7 +327,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
this.lastReturned = null;
this.finished = true;
- this.set.finishRawIterator();
+ this.set.finishRawIterator(); // Pufferfish - async mob spawning - diff on change
}
}
}

View File

@@ -5,23 +5,56 @@ Subject: [PATCH] PlayerInventoryOverflowEvent
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
index 19180c08f41db939c1a9f0caeb62e5beb1117f69..c765118189cbf8db7291c1d50214a7f31acc3a49 100644 index 19180c08f41db939c1a9f0caeb62e5beb1117f69..9b5c8c4c39657c18e67ad654bd0e5a30c09232c6 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
@@ -340,6 +340,16 @@ public class CraftInventory implements Inventory { @@ -340,9 +340,49 @@ public class CraftInventory implements Inventory {
} }
} }
} }
+ +
+ // Leaf start - PlayerInventoryOverflowEvent + // Leaf start - PlayerInventoryOverflowEvent
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0 + if (isListeningInventoryOverflowEvent()
+ && !leftover.isEmpty() && this.inventory instanceof net.minecraft.world.entity.player.Inventory && this.inventory.getOwner() instanceof org.bukkit.entity.Player player) { + && !leftover.isEmpty() && this.inventory instanceof net.minecraft.world.entity.player.Inventory && this.inventory.getOwner() instanceof org.bukkit.entity.Player player) {
+ new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(player, leftover).callEvent(); + new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(player, leftover).callEvent();
+ +
+ leftover = new HashMap<>(); + return new HashMap<>();
+ } + }
+ // Leaf end - PlayerInventoryOverflowEvent + // Leaf end - PlayerInventoryOverflowEvent
+ +
return leftover; return leftover;
} }
+ // Leaf start - PlayerInventoryOverflowEvent
+ private static boolean isListeningInventoryOverflowEvent() {
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached == -1) {
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 0;
+
+ if (!org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.enabled) {
+ return false;
+ }
+
+ org.bukkit.plugin.RegisteredListener[] listeners = org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners();
+ if (listeners.length == 1) {
+ if (listeners[0].getListener().getClass().getName().equals(org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.listenerClass)) {
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 1;
+ return true;
+ }
+ } else if (listeners.length > 1) {
+ for (org.bukkit.plugin.RegisteredListener registeredListener : listeners) {
+ if (registeredListener.getListener().getClass().getName().equals(org.dreeam.leaf.config.modules.gameplay.ConfigurableInventoryOverflowEvent.listenerClass)) {
+ org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached = 1;
+ return true;
+ }
+ ;
+ }
+ }
+ }
+
+ return org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.isListeningInvOverflowCached == 1;
+ }
+ // Leaf end - PlayerInventoryOverflowEvent
+
@Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) {
// Paper start

View File

@@ -0,0 +1,87 @@
package org.dreeam.leaf.async.ai;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dreeam.leaf.config.modules.async.AsyncTargetFinding;
import org.dreeam.leaf.util.queue.SpscIntQueue;
import java.util.concurrent.locks.LockSupport;
public class AsyncGoalExecutor {
public static final Logger LOGGER = LogManager.getLogger("Leaf Async Goal");
protected final SpscIntQueue queue;
protected final SpscIntQueue wake;
private final AsyncGoalThread thread;
private final ServerLevel serverLevel;
private boolean dirty = false;
private long tickCount = 0L;
private static final int SPIN_LIMIT = 100;
public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel serverLevel) {
this.serverLevel = serverLevel;
this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.thread = thread;
}
boolean wake(int id) {
Entity entity = this.serverLevel.getEntities().get(id);
if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) {
return false;
}
mob.goalSelector.wake();
mob.targetSelector.wake();
return true;
}
public final void submit(int entityId) {
if (!this.queue.send(entityId)) {
int spinCount = 0;
while (!this.queue.send(entityId)) {
spinCount++;
// Unpark the thread after some spinning to help clear the queue
if (spinCount > SPIN_LIMIT) {
unpark();
spinCount = 0;
}
Thread.onSpinWait();
}
}
dirty = true;
}
public final void unpark() {
if (dirty) LockSupport.unpark(thread);
dirty = false;
}
public final void midTick() {
boolean didWork = false;
while (true) {
int id = this.wake.recv();
if (id == Integer.MAX_VALUE) {
break;
}
didWork = true;
Entity entity = this.serverLevel.getEntities().get(id);
if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) {
continue;
}
mob.tickingTarget = true;
boolean a = mob.targetSelector.poll();
mob.tickingTarget = false;
boolean b = mob.goalSelector.poll();
if (a || b) {
submit(id);
}
}
if (didWork || (tickCount & 15L) == 0L) unpark();
tickCount += 1;
}
}

View File

@@ -0,0 +1,57 @@
package org.dreeam.leaf.async.ai;
import net.minecraft.Util;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import java.util.concurrent.locks.LockSupport;
public class AsyncGoalThread extends Thread {
private static final int SPIN_TRIES = 1000;
public AsyncGoalThread(final MinecraftServer server) {
super(() -> run(server), "Leaf Async Goal Thread");
this.setDaemon(false);
this.setUncaughtExceptionHandler(Util::onThreadException);
this.setPriority(Thread.NORM_PRIORITY - 1);
this.start();
}
private static void run(MinecraftServer server) {
int emptySpins = 0;
while (server.isRunning()) {
boolean didWork = false;
for (ServerLevel level : server.getAllLevels()) {
var exec = level.asyncGoalExecutor;
boolean levelWork = false;
while (true) {
int id = exec.queue.recv();
if (id == Integer.MAX_VALUE) {
break;
}
levelWork = true;
if (exec.wake(id)) {
while (!exec.wake.send(id)) {
Thread.onSpinWait();
}
}
}
didWork |= levelWork;
}
// Adaptive parking
if (didWork) {
emptySpins = 0; // Reset counter when work was done
} else {
emptySpins++;
if (emptySpins > SPIN_TRIES) {
LockSupport.park(); // Only park after several empty spins
emptySpins = 0;
} else {
Thread.onSpinWait(); // Yield to other threads but don't park
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
package org.dreeam.leaf.async.ai;
import org.jetbrains.annotations.Nullable;
public class Waker {
@Nullable
public volatile Runnable wake = null;
@Nullable
public volatile Object result = null;
public volatile boolean state = true;
public final @Nullable Object result() {
Object result = this.result;
this.result = null;
return result;
}
}

View File

@@ -6,7 +6,8 @@ import java.util.Locale;
public enum PathfindTaskRejectPolicy { public enum PathfindTaskRejectPolicy {
FLUSH_ALL, FLUSH_ALL,
CALLER_RUNS; CALLER_RUNS,
DISCARD;
public static PathfindTaskRejectPolicy fromString(String policy) { public static PathfindTaskRejectPolicy fromString(String policy) {
try { try {

View File

@@ -46,6 +46,13 @@ public abstract class ConfigModules extends LeafConfig {
for (ConfigModules module : MODULES) { for (ConfigModules module : MODULES) {
module.onPostLoaded(); module.onPostLoaded();
} }
// Save config to disk
try {
LeafConfig.config().saveConfig();
} catch (Exception e) {
LeafConfig.LOGGER.error("Failed to save config file!", e);
}
} }
private static List<Field> getAnnotatedStaticFields(Class<?> clazz, Class<? extends Annotation> annotation) { private static List<Field> getAnnotatedStaticFields(Class<?> clazz, Class<? extends Annotation> annotation) {

View File

@@ -58,6 +58,7 @@ public class LeafConfig {
/* Load & Reload */ /* Load & Reload */
// Reload config (async)
public static @NotNull CompletableFuture<Void> reloadAsync(CommandSender sender) { public static @NotNull CompletableFuture<Void> reloadAsync(CommandSender sender) {
return CompletableFuture.runAsync(() -> { return CompletableFuture.runAsync(() -> {
try { try {
@@ -76,6 +77,7 @@ public class LeafConfig {
}, Util.ioPool()); }, Util.ioPool());
} }
// Init config
public static void loadConfig() { public static void loadConfig() {
try { try {
long begin = System.nanoTime(); long begin = System.nanoTime();
@@ -100,9 +102,6 @@ public class LeafConfig {
// Load config modules // Load config modules
ConfigModules.initModules(); ConfigModules.initModules();
// Save config to disk
leafGlobalConfig.saveConfig();
} }
public static LeafGlobalConfig config() { public static LeafGlobalConfig config() {

View File

@@ -1,28 +0,0 @@
package org.dreeam.leaf.config.modules.async;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
public class AsyncBlockFinding extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-block-finding";
}
public static boolean enabled = false;
public static boolean asyncBlockFindingInitialized;
@Override
public void onLoaded() {
config.addCommentRegionBased(getBasePath(), """
This moves the expensive search calculations to a background thread while
keeping the actual block validation on the main thread.""",
"""
这会将昂贵的搜索计算移至后台线程, 同时在主线程上保持实际的方块验证.""");
if (!asyncBlockFindingInitialized) {
asyncBlockFindingInitialized = true;
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
}
}
}

View File

@@ -13,8 +13,7 @@ public class AsyncChunkSend extends ConfigModules {
@Override @Override
public void onLoaded() { public void onLoaded() {
config.addCommentRegionBased(getBasePath(), config.addCommentRegionBased(getBasePath(), """
"""
Makes chunk packet preparation and sending asynchronous to improve server performance. Makes chunk packet preparation and sending asynchronous to improve server performance.
This can significantly reduce main thread load when many players are loading chunks.""", This can significantly reduce main thread load when many players are loading chunks.""",
""" """

View File

@@ -10,7 +10,7 @@ public class AsyncMobSpawning extends ConfigModules {
} }
public static boolean enabled = true; public static boolean enabled = true;
public static boolean asyncMobSpawningInitialized; private static boolean asyncMobSpawningInitialized;
@Override @Override
public void onLoaded() { public void onLoaded() {
@@ -22,13 +22,16 @@ public class AsyncMobSpawning extends ConfigModules {
This just offloads some expensive calculations that are required for mob spawning.""", This just offloads some expensive calculations that are required for mob spawning.""",
""" """
是否异步化生物生成. 是否异步化生物生成.
在实体较多的服务器上, 异步生成可最高带来15%的性能提升. 在实体较多的服务器上, 异步生成可最高带来 15% 的性能提升.
须在Paper配置文件中打开 per-player-mob-spawns 才能生效."""); 须在Paper配置文件中打开 per-player-mob-spawns 才能生效.""");
// This prevents us from changing the value during a reload. // This prevents us from changing the value during a reload.
if (!asyncMobSpawningInitialized) { if (asyncMobSpawningInitialized) {
asyncMobSpawningInitialized = true; config.getConfigSection(getBasePath());
enabled = config.getBoolean(getBasePath() + ".enabled", enabled); return;
} }
asyncMobSpawningInitialized = true;
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
} }
} }

View File

@@ -16,9 +16,27 @@ public class AsyncPathfinding extends ConfigModules {
public static int asyncPathfindingKeepalive = 60; public static int asyncPathfindingKeepalive = 60;
public static int asyncPathfindingQueueSize = 0; public static int asyncPathfindingQueueSize = 0;
public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL; public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL;
private static boolean asyncPathfindingInitialized;
@Override @Override
public void onLoaded() { public void onLoaded() {
config.addCommentRegionBased(getBasePath() + ".reject-policy", """
The policy to use when the queue is full and a new task is submitted.
FLUSH_ALL: All pending tasks will be run on server thread.
CALLER_RUNS: Newly submitted task will be run on server thread.
DISCARD: Newly submitted task will be dropped directly.""",
"""
当队列满时, 新提交的任务将使用以下策略处理.
FLUSH_ALL: 所有等待中的任务都将在主线程上运行.
CALLER_RUNS: 新提交的任务将在主线程上运行.
DISCARD: 新提交的任务会被直接丢弃."""
);
if (asyncPathfindingInitialized) {
config.getConfigSection(getBasePath());
return;
}
asyncPathfindingInitialized = true;
final int availableProcessors = Runtime.getRuntime().availableProcessors(); final int availableProcessors = Runtime.getRuntime().availableProcessors();
enabled = config.getBoolean(getBasePath() + ".enabled", enabled); enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
asyncPathfindingMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncPathfindingMaxThreads); asyncPathfindingMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncPathfindingMaxThreads);
@@ -37,15 +55,10 @@ public class AsyncPathfinding extends ConfigModules {
if (asyncPathfindingQueueSize <= 0) if (asyncPathfindingQueueSize <= 0)
asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256; asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256;
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(config.getString(getBasePath() + ".reject-policy", availableProcessors >= 12 && asyncPathfindingQueueSize < 512 ? PathfindTaskRejectPolicy.FLUSH_ALL.toString() : PathfindTaskRejectPolicy.CALLER_RUNS.toString(), config.pickStringRegionBased( asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(config.getString(getBasePath() + ".reject-policy",
""" availableProcessors >= 12 && asyncPathfindingQueueSize < 512
The policy to use when the queue is full and a new task is submitted. ? PathfindTaskRejectPolicy.FLUSH_ALL.toString()
FLUSH_ALL: All pending tasks will be run on server thread. : PathfindTaskRejectPolicy.CALLER_RUNS.toString())
CALLER_RUNS: Newly submitted task will be run on server thread.""", );
"""
当队列满时, 新提交的任务将使用以下策略处理.
FLUSH_ALL: 所有等待中的任务都将在主线程上运行.
CALLER_RUNS: 新提交的任务将在主线程上运行."""
)));
} }
} }

View File

@@ -10,17 +10,13 @@ public class AsyncPlayerDataSave extends ConfigModules {
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-playerdata-save"; return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-playerdata-save";
} }
@Experimental
public static boolean enabled = false; public static boolean enabled = false;
@Override @Override
public void onLoaded() { public void onLoaded() {
config.addCommentRegionBased(getBasePath(), config.addCommentRegionBased(getBasePath(), """
"""
**Experimental feature, may have data lost in some circumstances!**
Make PlayerData saving asynchronously.""", Make PlayerData saving asynchronously.""",
""" """
**实验性功能, 在部分场景下可能丢失玩家数据!**
异步保存玩家数据."""); 异步保存玩家数据.""");
enabled = config.getBoolean(getBasePath() + ".enabled", enabled); enabled = config.getBoolean(getBasePath() + ".enabled", enabled);

View File

@@ -11,22 +11,40 @@ public class AsyncTargetFinding extends ConfigModules {
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-target-finding"; return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-target-finding";
} }
@Experimental
public static boolean enabled = false; public static boolean enabled = false;
public static boolean asyncTargetFindingInitialized; public static boolean alertOther = true;
public static boolean searchBlock = true;
public static boolean searchEntity = true;
public static int queueSize = 4096;
private static boolean asyncTargetFindingInitialized;
@Override @Override
public void onLoaded() { public void onLoaded() {
config.addCommentRegionBased(getBasePath(), """ config.addCommentRegionBased(getBasePath(), """
**Experimental feature** This moves the expensive entity and block search calculations to background thread while
This moves the expensive entity target search calculations to a background thread while keeping the actual validation on the main thread.""",
keeping the actual entity validation on the main thread.""",
""" """
这会将昂贵的实体目标搜索计算移至后台线程, 同时在主线程上保持实际的实体验证."""); 这会将昂贵的实体目标搜索计算移至后台线程, 同时在主线程上保持实际的实体验证.""");
if (!asyncTargetFindingInitialized) { if (asyncTargetFindingInitialized) {
asyncTargetFindingInitialized = true; config.getConfigSection(getBasePath());
enabled = config.getBoolean(getBasePath() + ".enabled", enabled); return;
}
asyncTargetFindingInitialized = true;
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true);
searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true);
searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true);
queueSize = config.getInt(getBasePath() + ".queue-size", 4096);
if (queueSize <= 0) {
queueSize = 4096;
}
if (!enabled) {
alertOther = false;
searchEntity = false;
searchBlock = false;
} }
} }
} }

View File

@@ -15,6 +15,7 @@ public class MultithreadedTracker extends ConfigModules {
public static int asyncEntityTrackerMaxThreads = 0; public static int asyncEntityTrackerMaxThreads = 0;
public static int asyncEntityTrackerKeepalive = 60; public static int asyncEntityTrackerKeepalive = 60;
public static int asyncEntityTrackerQueueSize = 0; public static int asyncEntityTrackerQueueSize = 0;
private static boolean asyncMultithreadedTrackerInitialized;
@Override @Override
public void onLoaded() { public void onLoaded() {
@@ -24,15 +25,22 @@ public class MultithreadedTracker extends ConfigModules {
""" """
异步实体跟踪, 异步实体跟踪,
在实体数量多且密集的情况下效果明显."""); 在实体数量多且密集的情况下效果明显.""");
config.addCommentRegionBased(getBasePath() + ".compat-mode", """
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled, config.pickStringRegionBased("""
Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed, Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed,
Compat mode fixed visible issue with player type NPCs of Citizens, Compat mode fixed visible issue with player type NPCs of Citizens,
But still recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC or else.""", But still recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC or else.""",
""" """
是否启用兼容模式, 是否启用兼容模式,
如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项.""")); 如果你的服务器安装了 Citizens 或其他类似非发包 NPC 插件, 请开启此项.""");
if (asyncMultithreadedTrackerInitialized) {
config.getConfigSection(getBasePath());
return;
}
asyncMultithreadedTrackerInitialized = true;
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
compatModeEnabled = config.getBoolean(getBasePath() + ".compat-mode", compatModeEnabled);
asyncEntityTrackerMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncEntityTrackerMaxThreads); asyncEntityTrackerMaxThreads = config.getInt(getBasePath() + ".max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = config.getInt(getBasePath() + ".keepalive", asyncEntityTrackerKeepalive); asyncEntityTrackerKeepalive = config.getInt(getBasePath() + ".keepalive", asyncEntityTrackerKeepalive);
asyncEntityTrackerQueueSize = config.getInt(getBasePath() + ".queue-size", asyncEntityTrackerQueueSize); asyncEntityTrackerQueueSize = config.getInt(getBasePath() + ".queue-size", asyncEntityTrackerQueueSize);

View File

@@ -2,6 +2,7 @@ package org.dreeam.leaf.config.modules.async;
import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory; import org.dreeam.leaf.config.EnumConfigCategory;
import org.dreeam.leaf.config.LeafConfig;
import org.dreeam.leaf.config.annotations.Experimental; import org.dreeam.leaf.config.annotations.Experimental;
public class SparklyPaperParallelWorldTicking extends ConfigModules { public class SparklyPaperParallelWorldTicking extends ConfigModules {
@@ -19,22 +20,29 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules {
@Override @Override
public void onLoaded() { public void onLoaded() {
config.addCommentRegionBased(getBasePath(), config.addCommentRegionBased(getBasePath(), """
"""
**Experimental feature** **Experimental feature**
Enables parallel world ticking to improve performance on multi-core systems..""", Enables parallel world ticking to improve performance on multi-core systems.""",
""" """
**实验性功能** **实验性功能**
启用并行世界处理以提高多核系统的性能."""); 启用并行世界处理以提高多核 CPU 使用率.""");
enabled = config.getBoolean(getBasePath() + ".enabled", enabled); enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
threads = config.getInt(getBasePath() + ".threads", threads); threads = config.getInt(getBasePath() + ".threads", threads);
threads = enabled ? threads : 0; if (enabled) {
if (threads <= 0) threads = 8;
} else {
threads = 0;
}
logContainerCreationStacktraces = config.getBoolean(getBasePath() + ".log-container-creation-stacktraces", logContainerCreationStacktraces); logContainerCreationStacktraces = config.getBoolean(getBasePath() + ".log-container-creation-stacktraces", logContainerCreationStacktraces);
logContainerCreationStacktraces = enabled && logContainerCreationStacktraces; logContainerCreationStacktraces = enabled && logContainerCreationStacktraces;
disableHardThrow = config.getBoolean(getBasePath() + ".disable-hard-throw", disableHardThrow); disableHardThrow = config.getBoolean(getBasePath() + ".disable-hard-throw", disableHardThrow);
disableHardThrow = enabled && disableHardThrow; disableHardThrow = enabled && disableHardThrow;
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", runAsyncTasksSync); runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", runAsyncTasksSync);
runAsyncTasksSync = enabled && runAsyncTasksSync; runAsyncTasksSync = enabled && runAsyncTasksSync;
if (enabled) {
LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads);
}
} }
} }

View File

@@ -0,0 +1,32 @@
package org.dreeam.leaf.config.modules.gameplay;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
public class ConfigurableInventoryOverflowEvent extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".inventory-overflow-event";
}
public static boolean enabled = false;
public static String listenerClass = "com.example.package.PlayerInventoryOverflowEvent" ;
@Override
public void onLoaded() {
enabled = config.getBoolean(getBasePath() + ".enabled", enabled, config.pickStringRegionBased("""
The event called when used plugin to Inventory#addItem
into player's inventory, and the inventory is full.
This is not recommended to use, please re-design to use the
returned map of Inventory#addItem method as soon as possible!""",
"""
此事件将在插件使用 Inventory#addItem 方法
添加物品到玩家背包, 但是背包已满时调用.
不建议使用此事件,请尽快迁移至使用 Inventory#addItem 方法
返回的 map"""));
listenerClass = config.getString(getBasePath() + ".listener-class", listenerClass, config.pickStringRegionBased("""
The full class name of the listener which listens to this inventory overflow event.""",
"""
监听此物品栏物品溢出事件的完整类名."""));
}
}

View File

@@ -15,8 +15,7 @@ public class SmoothTeleport extends ConfigModules {
@Override @Override
public void onLoaded() { public void onLoaded() {
enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased( enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased("""
"""
**Experimental feature** **Experimental feature**
Whether to make a "smooth teleport" when players changing dimension. Whether to make a "smooth teleport" when players changing dimension.
This requires original world and target world have same logical height to work.""", This requires original world and target world have same logical height to work.""",

View File

@@ -10,7 +10,7 @@ public class ServerBrand extends ConfigModules {
} }
public static String serverModName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); public static String serverModName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName();
public static String serverGUIName = "Leaf Console"; public static String serverGUIName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName() + " Console";
@Override @Override
public void onLoaded() { public void onLoaded() {

View File

@@ -17,8 +17,7 @@ public class ChatMessageSignature extends ConfigModules {
Whether or not enable chat message signature, Whether or not enable chat message signature,
disable will prevent players to report chat messages. disable will prevent players to report chat messages.
And also disables the popup when joining a server without And also disables the popup when joining a server without
'secure chat', such as offline-mode servers. 'secure chat', such as offline-mode servers.""",
""",
""" """
是否启用聊天签名, 禁用后玩家无法进行聊天举报.""")); 是否启用聊天签名, 禁用后玩家无法进行聊天举报."""));
} }

View File

@@ -20,8 +20,7 @@ public class OptimizeNonFlushPacketSending extends ConfigModules {
Optimizes non-flush packet sending by using Netty's lazyExecute method to avoid Optimizes non-flush packet sending by using Netty's lazyExecute method to avoid
expensive thread wakeup calls when scheduling packet operations. expensive thread wakeup calls when scheduling packet operations.
Requires server restart to take effect. Requires server restart to take effect.""",
""",
""" """
警告: 此选项与 ProtocolLib 不兼容, 并可能导致与其他修改数据包 警告: 此选项与 ProtocolLib 不兼容, 并可能导致与其他修改数据包
处理的插件出现问题. 处理的插件出现问题.
@@ -29,7 +28,6 @@ public class OptimizeNonFlushPacketSending extends ConfigModules {
通过使用 Netty 的 lazyExecute 方法来优化非刷新数据包的发送, 通过使用 Netty 的 lazyExecute 方法来优化非刷新数据包的发送,
避免在调度数据包操作时进行昂贵的线程唤醒调用. 避免在调度数据包操作时进行昂贵的线程唤醒调用.
需要重启服务器才能生效. 需要重启服务器才能生效."""));
"""));
} }
} }

View File

@@ -15,8 +15,7 @@ public class DontSaveEntity extends ConfigModules {
@Override @Override
public void onLoaded() { public void onLoaded() {
dontSavePrimedTNT = config.getBoolean(getBasePath() + ".dont-save-primed-tnt", dontSavePrimedTNT, dontSavePrimedTNT = config.getBoolean(getBasePath() + ".dont-save-primed-tnt", dontSavePrimedTNT,
config.pickStringRegionBased( config.pickStringRegionBased("""
"""
Disable save primed tnt on chunk unloads. Disable save primed tnt on chunk unloads.
Useful for redstone/technical servers, can prevent machines from being exploded by TNT, Useful for redstone/technical servers, can prevent machines from being exploded by TNT,
when player disconnected caused by Internet issue.""", when player disconnected caused by Internet issue.""",

View File

@@ -67,8 +67,8 @@ public class FastRNG extends ConfigModules {
Use direct random implementation instead of delegating to Java's RandomGenerator. Use direct random implementation instead of delegating to Java's RandomGenerator.
This may improve performance but potentially changes RNG behavior.""", This may improve performance but potentially changes RNG behavior.""",
""" """
使用直接随机实现而不是委托给Java的RandomGenerator. 使用直接随机实现而不是委派给 RandomGenerator.
这可能会提高性能但可能会改变RNG行为""")); 这可能会提高性能, 但可能会改变 RNG 行为."""));
if (enabled) { if (enabled) {
try { try {

View File

@@ -10,7 +10,7 @@ public class ThrottleHopperWhenFull extends ConfigModules {
} }
public static boolean enabled = false; public static boolean enabled = false;
public static int skipTicks = 0; public static int skipTicks = 8;
@Override @Override
public void onLoaded() { public void onLoaded() {

View File

@@ -0,0 +1,70 @@
package org.dreeam.leaf.util.map;
import com.google.common.collect.AbstractIterator;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public final class BlockPosIterator extends AbstractIterator<BlockPos> {
private final int startX;
private final int startY;
private final int startZ;
private final int endX;
private final int endY;
private final int endZ;
private @Nullable MutableBlockPos pos = null;
public static Iterable<BlockPos> iterable(AABB bb) {
return () -> new BlockPosIterator(bb);
}
public static Iterable<BlockPos> traverseArea(Vec3 vec, AABB boundingBox) {
double toTravel = Math.min(16.0 / vec.length(), 1.0);
Vec3 movement = vec.scale(toTravel);
AABB fromBB = boundingBox.move(-vec.x, -vec.y, -vec.z);
AABB searchArea = fromBB.expandTowards(movement);
return BlockPosIterator.iterable(searchArea);
}
public BlockPosIterator(AABB bb) {
this.startX = Mth.floor(bb.minX);
this.startY = Mth.floor(bb.minY);
this.startZ = Mth.floor(bb.minZ);
this.endX = Mth.floor(bb.maxX);
this.endY = Mth.floor(bb.maxY);
this.endZ = Mth.floor(bb.maxZ);
}
@Override
protected BlockPos computeNext() {
MutableBlockPos pos = this.pos;
if (pos == null) {
return this.pos = new MutableBlockPos(this.startX, this.startY, this.startZ);
} else {
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
if (y < this.endY) {
y += 1;
} else if (x < this.endX) {
x += 1;
y = this.startY;
} else if (z < this.endZ) {
z += 1;
x = this.startX;
} else {
return this.endOfData();
}
pos.set(x, y, z);
return pos;
}
}
}

View File

@@ -1,6 +1,9 @@
package org.dreeam.leaf.util.map; package org.dreeam.leaf.util.map;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.LongCollection;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;

View File

@@ -20,20 +20,18 @@ public class FasterRandomSource implements BitRandomSource {
private static final RandomGeneratorFactory<RandomGenerator> RANDOM_GENERATOR_FACTORY = RandomGeneratorFactory.of(FastRNG.randomGenerator); private static final RandomGeneratorFactory<RandomGenerator> RANDOM_GENERATOR_FACTORY = RandomGeneratorFactory.of(FastRNG.randomGenerator);
private static final boolean isSplittableGenerator = RANDOM_GENERATOR_FACTORY.isSplittable(); private static final boolean isSplittableGenerator = RANDOM_GENERATOR_FACTORY.isSplittable();
private long seed; private long seed;
private boolean useDirectImpl; private static final boolean useDirectImpl = FastRNG.useDirectImpl;
private RandomGenerator randomGenerator; private RandomGenerator randomGenerator;
public static final FasterRandomSource SHARED_INSTANCE = new FasterRandomSource(ThreadLocalRandom.current().nextLong()); public static final FasterRandomSource SHARED_INSTANCE = new FasterRandomSource(ThreadLocalRandom.current().nextLong());
public FasterRandomSource(long seed) { public FasterRandomSource(long seed) {
this.seed = seed; this.seed = seed;
this.randomGenerator = RANDOM_GENERATOR_FACTORY.create(seed); this.randomGenerator = RANDOM_GENERATOR_FACTORY.create(seed);
this.useDirectImpl = FastRNG.useDirectImpl; // Get the value from config
} }
private FasterRandomSource(long seed, RandomGenerator.SplittableGenerator randomGenerator) { private FasterRandomSource(long seed, RandomGenerator.SplittableGenerator randomGenerator) {
this.seed = seed; this.seed = seed;
this.randomGenerator = randomGenerator; this.randomGenerator = randomGenerator;
this.useDirectImpl = FastRNG.useDirectImpl;
} }
@Override @Override
@@ -59,7 +57,6 @@ public class FasterRandomSource implements BitRandomSource {
@Override @Override
public final int next(int bits) { public final int next(int bits) {
if (useDirectImpl) { if (useDirectImpl) {
// Direct
return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits)); return (int) ((seed = seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> (INT_BITS - bits));
} }

View File

@@ -0,0 +1,64 @@
package org.dreeam.leaf.util.queue;
/// Lock-free Single Producer Single Consumer Queue
public class SpscIntQueue {
private final int[] data;
private final PaddedAtomicInteger producerIdx = new PaddedAtomicInteger();
private final PaddedAtomicInteger producerCachedIdx = new PaddedAtomicInteger();
private final PaddedAtomicInteger consumerIdx = new PaddedAtomicInteger();
private final PaddedAtomicInteger consumerCachedIdx = new PaddedAtomicInteger();
public SpscIntQueue(int size) {
this.data = new int[size + 1];
}
public final boolean send(int e) {
final int idx = producerIdx.getOpaque();
int nextIdx = idx + 1;
if (nextIdx == data.length) {
nextIdx = 0;
}
int cachedIdx = consumerCachedIdx.getPlain();
if (nextIdx == cachedIdx) {
cachedIdx = consumerIdx.getAcquire();
consumerCachedIdx.setPlain(cachedIdx);
if (nextIdx == cachedIdx) {
return false;
}
}
data[idx] = e;
producerIdx.setRelease(nextIdx);
return true;
}
public final int recv() {
final int idx = consumerIdx.getOpaque();
int cachedIdx = producerCachedIdx.getPlain();
if (idx == cachedIdx) {
cachedIdx = producerIdx.getAcquire();
producerCachedIdx.setPlain(cachedIdx);
if (idx == cachedIdx) {
return Integer.MAX_VALUE;
}
}
int e = data[idx];
int nextIdx = idx + 1;
if (nextIdx == data.length) {
nextIdx = 0;
}
consumerIdx.setRelease(nextIdx);
return e;
}
public final int size() {
return this.data.length;
}
static class PaddedAtomicInteger extends java.util.concurrent.atomic.AtomicInteger {
@SuppressWarnings("unused")
private int i1, i2, i3, i4, i5, i6, i7, i8,
i9, i10, i11, i12, i13, i14, i15;
}
}

View File

@@ -11,4 +11,4 @@
- [ ] Check beacon issues fix - [ ] Check beacon issues fix
- [ ] Update README.md - [ ] Update README.md
- [ ] Remove stream in Inventory and check new changes - [ ] Remove stream in Inventory and check new changes
- [ ] Update from Leaf 1.21.4 (curr commit: `dd22d9cafffece7d469a7d702f63952abea441d2`) - [ ] Update from Leaf 1.21.4 (curr commit: `1431eff510a3ac1725ec82d52f5c253b4b7932d9`)