9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-30 20:39:21 +00:00
This commit is contained in:
Dreeam
2025-03-28 16:22:53 -04:00
parent 233da80a87
commit 2f8915ce2b
13 changed files with 158 additions and 181 deletions

View File

@@ -7,31 +7,32 @@ Original license: GPL v3
Original project: https://github.com/Gensokyo-Reimagined/Nitori
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 5a0d30b8ff5e377224de67c9f464bd1c694a4397..b316c174963cd395186d45f3fb9ca78470fa5c27 100644
index 5a0d30b8ff5e377224de67c9f464bd1c694a4397..1cb60107d95296fc9e2c106d70838c057564abeb 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1069,6 +1069,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
io.papermc.paper.log.CustomLogManager.forceReset(); // Paper - Reset loggers after shutdown
this.onServerExit();
// Paper end - Improved watchdog support - move final shutdown items here
+ // Leaf start
+ // Leaf start - Async playerdata saving
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.IO_POOL.shutdown();
+ try {
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.IO_POOL.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS);
+ } catch (java.lang.InterruptedException ignored) {}
+ // Leaf end
+ // Leaf end - Async playerdata saving
}
public String getLocalIp() {
diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java
index de43e54698125ce9f319d4889dd49f7029fe95e0..4e6737636f754c9e18cb9a2c9e2f3a9ea8b9d02c 100644
index de43e54698125ce9f319d4889dd49f7029fe95e0..451cb8987906f0a9e753a870aa629124daba9c73 100644
--- a/net/minecraft/world/level/storage/LevelStorageSource.java
+++ b/net/minecraft/world/level/storage/LevelStorageSource.java
@@ -520,15 +520,25 @@ public class LevelStorageSource {
@@ -520,15 +520,26 @@ public class LevelStorageSource {
private void saveLevelData(CompoundTag tag) {
Path path = this.levelDirectory.path();
+ // Leaf start - Async IO
+ // Leaf start - Async playerdata saving
+ // Save level.dat asynchronously
+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
try {
- Path path1 = Files.createTempFile(path, "level", ".dat");
@@ -55,27 +56,27 @@ index de43e54698125ce9f319d4889dd49f7029fe95e0..4e6737636f754c9e18cb9a2c9e2f3a9e
+ LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6);
+ }
+ });
+ // Leaf end
+ // Leaf end - Async playerdata saving
}
public Optional<Path> getIconFile() {
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
index c44110b123ba5912af18faf0065e9ded780da9b7..0c3ba0b8ea8b1b5b66943c40b0f00bf998cf5f18 100644
index c44110b123ba5912af18faf0065e9ded780da9b7..541c440d3211c6625974ac1e0055d62655256846 100644
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -25,6 +25,7 @@ public class PlayerDataStorage {
private final File playerDir;
protected final DataFixer fixerUpper;
private static final DateTimeFormatter FORMATTER = FileNameDateFormatter.create();
+ public final it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<String> saving = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); // Leaf
+ public final it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<java.util.UUID> savingQueue = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); // Leaf - Async playerdata saving
public PlayerDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) {
this.fixerUpper = fixerUpper;
@@ -34,17 +35,42 @@ public class PlayerDataStorage {
@@ -34,17 +35,43 @@ public class PlayerDataStorage {
public void save(Player player) {
if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
+ // Leaf start - Async IO
+ // Leaf start - Async playerdata saving
+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
try {
CompoundTag compoundTag = player.saveWithoutId(new CompoundTag());
@@ -93,14 +94,15 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..0c3ba0b8ea8b1b5b66943c40b0f00bf9
}
+ String playerName = player.getScoreboardName();
+ String stringUuid = player.getStringUUID();
+ java.util.UUID playerUuid = player.getUUID();
+ synchronized (PlayerDataStorage.this) {
+ while (saving.contains(stringUuid)) {
+ while (savingQueue.contains(playerUuid)) {
+ try {
+ Thread.sleep(1L);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ saving.add(stringUuid);
+ savingQueue.add(playerUuid);
+ }
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.save(() -> {
+ try {
@@ -114,28 +116,70 @@ index c44110b123ba5912af18faf0065e9ded780da9b7..0c3ba0b8ea8b1b5b66943c40b0f00bf9
+ LOGGER.warn("Failed to save player data for {}", playerName, var7); // Paper - Print exception
+ } finally {
+ synchronized (PlayerDataStorage.this) {
+ saving.remove(stringUuid);
+ savingQueue.remove(playerUuid);
+ }
+ }
+ });
+ // Leaf end
+ // Leaf end - Async playerdata saving
}
private void backup(String name, String stringUuid, String suffix) { // CraftBukkit
@@ -61,6 +87,16 @@ public class PlayerDataStorage {
@@ -60,7 +87,20 @@ public class PlayerDataStorage {
}
}
private Optional<CompoundTag> load(String name, String stringUuid, String suffix) { // CraftBukkit
+ // Leaf start
- private Optional<CompoundTag> load(String name, String stringUuid, String suffix) { // CraftBukkit
+ // Leaf start - Async playerdata saving
+ private Optional<CompoundTag> load(String name, String stringUuid, String suffix) {
+ return load(name, stringUuid, suffix, java.util.UUID.fromString(stringUuid));
+ }
+ private Optional<CompoundTag> load(String name, String stringUuid, String suffix, java.util.UUID playerUuid) { // CraftBukkit
+ synchronized (PlayerDataStorage.this) {
+ while (saving.contains(stringUuid)) {
+ while (savingQueue.contains(playerUuid)) {
+ try {
+ Thread.sleep(1L);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ // Leaf end
+ // Leaf end - Async playerdata saving
File file = new File(this.playerDir, stringUuid + suffix); // CraftBukkit
// Spigot start
boolean usingWrongFile = false;
@@ -91,7 +131,7 @@ public class PlayerDataStorage {
public Optional<CompoundTag> load(Player player) {
// CraftBukkit start
- return this.load(player.getName().getString(), player.getStringUUID()).map((tag) -> {
+ return this.load(player.getName().getString(), player.getStringUUID(), player.getUUID()).map((tag) -> { // Leaf - Async playerdata saving
if (player instanceof ServerPlayer serverPlayer) {
CraftPlayer craftPlayer = serverPlayer.getBukkitEntity();
// Only update first played if it is older than the one we have
@@ -106,20 +146,25 @@ public class PlayerDataStorage {
});
}
+ // Leaf start - Async playerdata saving
public Optional<CompoundTag> load(String name, String uuid) {
+ return this.load(name, uuid, java.util.UUID.fromString(uuid));
+ }
+ public Optional<CompoundTag> load(String name, String uuid, java.util.UUID playerUuid) {
// CraftBukkit end
- Optional<CompoundTag> optional = this.load(name, uuid, ".dat"); // CraftBukkit
+ Optional<CompoundTag> optional = this.load(name, uuid, ".dat", playerUuid); // CraftBukkit
if (optional.isEmpty()) {
this.backup(name, uuid, ".dat"); // CraftBukkit
}
- return optional.or(() -> this.load(name, uuid, ".dat_old")).map(compoundTag -> { // CraftBukkit
+ return optional.or(() -> this.load(name, uuid, ".dat_old", playerUuid)).map(compoundTag -> { // CraftBukkit
int dataVersion = NbtUtils.getDataVersion(compoundTag, -1);
compoundTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, compoundTag, dataVersion, net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion()); // Paper - rewrite data conversion system
// player.load(compoundTag); // CraftBukkit - handled above
return compoundTag;
});
}
+ // Leaf end - Async playerdata saving
// CraftBukkit start
public File getPlayerDir() {

View File

@@ -7,10 +7,10 @@ Original license: AGPL-3.0
Original project: https://github.com/snackbag/TT20
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index cc2bf337da1c240b82dc722970d6bbaf57331328..3597f7dd2d71fe136604518985e3d14461a6aad4 100644
index 1cb60107d95296fc9e2c106d70838c057564abeb..c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1550,6 +1550,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1556,6 +1556,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.server.spark.tickStart(); // Paper - spark
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - Server Tick Events

View File

@@ -265,7 +265,7 @@ index 5ab2c8333178335515e619b87ae420f948c83bd1..be3b2f023897a8823560ee059cb16ec9
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 8744690b69be75f9dbcfe5a52bb90ed387eaffd7..f2d1025a13649c35991a662c267a7653e75d19a2 100644
index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11b57ae175 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -291,6 +291,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -326,7 +326,7 @@ index 8744690b69be75f9dbcfe5a52bb90ed387eaffd7..f2d1025a13649c35991a662c267a7653
}
@Override
@@ -1671,6 +1684,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1677,6 +1690,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -345,7 +345,7 @@ index 8744690b69be75f9dbcfe5a52bb90ed387eaffd7..f2d1025a13649c35991a662c267a7653
protected void tickChildren(BooleanSupplier hasTimeLeft) {
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
@@ -1737,28 +1762,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1743,28 +1768,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
@@ -414,7 +414,7 @@ index 8744690b69be75f9dbcfe5a52bb90ed387eaffd7..f2d1025a13649c35991a662c267a7653
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
this.tickConnection();
@@ -1838,6 +1885,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1844,6 +1891,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
newLevels.remove(level.dimension());

View File

@@ -6,10 +6,10 @@ Subject: [PATCH] SparklyPaper: Track each world MSPT
Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index f2d1025a13649c35991a662c267a7653e75d19a2..7739b4955dcb489c6bba9c9db65ba87025f7c669 100644
index ac751d460ae0c8dbb858c4047c459a11b57ae175..24926aa7ed5c78b235659daf18b224b14beb744c 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1687,7 +1687,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1693,7 +1693,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Leaf start - SparklyPaper - parallel world ticking mod (move level ticking logic out for branch convergence)
private void tickLevel(ServerLevel serverLevel, BooleanSupplier hasTimeLeft) {
try {

View File

@@ -3,25 +3,34 @@ From: Taiyou06 <kaandindar21@gmail.com>
Date: Sat, 22 Mar 2025 12:51:28 +0100
Subject: [PATCH] Remove streams on PlayerDetector
Dreeam TODO: Merge to single loop
diff --git a/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java b/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java
index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5a500403a 100644
index 774d4028f0be7c388abb47f8eb97011341f50f59..81d2dfdd8bd2a8b205e4b617911c277a366f0369 100644
--- a/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java
+++ b/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java
@@ -21,27 +21,42 @@ import net.minecraft.world.phys.Vec3;
@@ -21,28 +21,45 @@ import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
public interface PlayerDetector {
- PlayerDetector NO_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> entitySelector.getPlayers(
+ PlayerDetector NO_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> {
+ List<? extends Player> players = entitySelector.getPlayers(
level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isCreative() && !player.isSpectator()
- level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isCreative() && !player.isSpectator()
- )
- .stream()
- .filter(player -> !requireLineOfSight || inLineOfSight(level, pos.getCenter(), player.getEyePosition()))
- .map(Entity::getUUID)
- .toList();
- PlayerDetector INCLUDING_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> entitySelector.getPlayers(
- level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isSpectator()
- )
- .stream()
- .filter(player -> !requireLineOfSight || inLineOfSight(level, pos.getCenter(), player.getEyePosition()))
- .map(Entity::getUUID)
- .toList();
+ // Leaf start - Remove streams on PlayerDetector
+ PlayerDetector NO_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> {
+ List<? extends Player> players = entitySelector.getPlayers(
+ level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isCreative() && !player.isSpectator()
+ );
+ List<UUID> result = new java.util.ArrayList<>();
+ for (Player player : players) {
@@ -34,12 +43,7 @@ index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5
+
+ PlayerDetector INCLUDING_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> {
+ List<? extends Player> players = entitySelector.getPlayers(
level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isSpectator()
- )
- .stream()
- .filter(player -> !requireLineOfSight || inLineOfSight(level, pos.getCenter(), player.getEyePosition()))
- .map(Entity::getUUID)
- .toList();
+ level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isSpectator()
+ );
+ List<UUID> result = new java.util.ArrayList<>();
+ for (Player player : players) {
@@ -66,13 +70,16 @@ index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5
+ }
+ return result;
};
+ // Leaf end - Remove streams on PlayerDetector
List<UUID> detect(ServerLevel level, PlayerDetector.EntitySelector entitySelector, BlockPos pos, double maxDistance, boolean flag);
@@ -78,14 +93,27 @@ public interface PlayerDetector {
@@ -78,14 +95,31 @@ public interface PlayerDetector {
return new PlayerDetector.EntitySelector() {
@Override
public List<Player> getPlayers(ServerLevel level, Predicate<? super Player> predicate) {
- return players.stream().filter(predicate).toList();
+ // Leaf start - Remove streams on PlayerDetector
+ List<Player> result = new java.util.ArrayList<>();
+ for (Player player : players) {
+ if (predicate.test(player)) {
@@ -80,6 +87,7 @@ index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5
+ }
+ }
+ return result;
+ // Leaf end - Remove streams on PlayerDetector
}
@Override
@@ -87,6 +95,7 @@ index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5
ServerLevel level, EntityTypeTest<Entity, T> typeTest, AABB boundingBox, Predicate<? super T> predicate
) {
- return players.stream().map(typeTest::tryCast).filter(Objects::nonNull).filter(predicate).toList();
+ // Leaf start - Remove streams on PlayerDetector
+ List<T> result = new java.util.ArrayList<>();
+ for (Player player : players) {
+ T entity = typeTest.tryCast(player);
@@ -95,6 +104,7 @@ index 774d4028f0be7c388abb47f8eb97011341f50f59..1597af2092e993efb1c02f92ed14d8c5
+ }
+ }
+ return result;
+ // Leaf end - Remove streams on PlayerDetector
}
};
}

View File

@@ -5,50 +5,36 @@ 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..51a949dbb88d7fff076379962000b70acecacce3 100644
index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..2bc0f19b86067491f33f647d2e387acd83492844 100644
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
@@ -1,9 +1,16 @@
package net.minecraft.world.entity.ai.goal;
import java.util.EnumSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.level.LevelReader;
+import org.dreeam.leaf.config.modules.async.AsyncBlockFinding;
public abstract class MoveToBlockGoal extends Goal {
private static final int GIVE_UP_TICKS = 1200;
@@ -20,6 +27,17 @@ public abstract class MoveToBlockGoal extends Goal {
@@ -20,6 +20,19 @@ public abstract class MoveToBlockGoal extends Goal {
private final int verticalSearchRange;
protected int verticalSearchStart;
+ private static final ExecutorService BLOCK_FINDER_EXECUTOR =
+ Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setNameFormat("block-finder-%d")
+ // 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 ConcurrentLinkedQueue<BlockPos> candidateBlocks = new ConcurrentLinkedQueue<>();
+ private final java.util.concurrent.ConcurrentLinkedQueue<BlockPos> candidateBlocks = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private boolean asyncSearchInProgress = false;
+ private boolean searchComplete = false;
+ // Leaf end - Async Block Finding
+
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) {
this(mob, speedModifier, searchRange, 1);
}
@@ -109,6 +127,92 @@ public abstract class MoveToBlockGoal extends Goal {
@@ -109,6 +122,95 @@ public abstract class MoveToBlockGoal extends Goal {
}
protected boolean findNearestBlock() {
+ // Leaf start - Async Block Finding
+ // Check if async is enabled, if not use the original implementation
+ if (!AsyncBlockFinding.enabled) {
+ if (!org.dreeam.leaf.config.modules.async.AsyncBlockFinding.enabled) {
+ return findNearestBlockSync();
+ }
+
@@ -59,6 +45,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..51a949dbb88d7fff076379962000b70a
+ candidateBlocks.clear();
+ return false;
+ }
+
+ while (!candidateBlocks.isEmpty()) {
+ BlockPos pos = candidateBlocks.poll();
+
@@ -105,7 +92,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..51a949dbb88d7fff076379962000b70a
+ // Generate candidate blocks in a spiral pattern
+ private void generateCandidateBlocks(BlockPos center, int searchRange, int verticalRange, int verticalStart) {
+ // Pre-calculate a prioritized list of positions
+ List<BlockPos> positions = new ArrayList<>();
+ 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++) {
@@ -133,6 +120,7 @@ index 3f080b15543bf8c5fa0774b62d7f12e13b82511a..51a949dbb88d7fff076379962000b70a
+
+ // The original method renamed
+ protected boolean findNearestBlockSync() {
+ // Leaf end - Async Block Finding
int i = this.searchRange;
int i1 = this.verticalSearchRange;
BlockPos blockPos = this.mob.blockPosition();

View File

@@ -5,25 +5,20 @@ Subject: [PATCH] Use direct iteration on Sensing.tick
diff --git a/net/minecraft/world/entity/ai/sensing/Sensing.java b/net/minecraft/world/entity/ai/sensing/Sensing.java
index cc25f5838aec5ed9fca2fb8b0322fafad9397a46..2ea5095a4290f5236caad7ea30a59d9b3eb32aaa 100644
index cc25f5838aec5ed9fca2fb8b0322fafad9397a46..002d3c0d8b1107a275020d5c582c37e9a5c536ee 100644
--- a/net/minecraft/world/entity/ai/sensing/Sensing.java
+++ b/net/minecraft/world/entity/ai/sensing/Sensing.java
@@ -34,12 +34,16 @@ public class Sensing {
public void tick() {
if (this.expiring == null) { // Gale - Petal - reduce line of sight updates
- this.seen.clear();
+ this.seen.clear();
// Gale start - Petal - reduce line of sight updates
@@ -39,7 +39,12 @@ public class Sensing {
} else {
var expiringNow = this.expiring[this.nextToExpireIndex];
- expiringNow.forEach(this.seen::remove);
+ // Leaf - use direct iteration
+ // Leaf start - Use direct iteration on Sensing.tick
+ var iterator = expiringNow.iterator();
+ while (iterator.hasNext()) {
+ this.seen.remove(iterator.nextInt());
+ }
+ // Leaf end - Use direct iteration on Sensing.tick
expiringNow.clear();
this.currentCacheAddIndex++;

View File

@@ -1,7 +1,11 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Wed, 26 Mar 2025 17:33:30 +0100
Subject: [PATCH] Optimize NonFlush PacketSending
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 22 Sep 2020 01:49:19 -0700
Subject: [PATCH] Optimise non-flush packet sending
Original license: GPLv3
Original project: https://github.com/PaperMC/Paper
Paper pull request: https://github.com/PaperMC/Paper/pull/10172
Places like entity tracking make heavy use of packet sending,
and internally netty will use some very expensive thread wakeup
@@ -22,14 +26,14 @@ Locally this patch drops the entity tracker tick by a full 1.5x.
Co-authored-by: Quang Tran <3d7777456@gmail.com>
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 5b46036868b6c9d082e35591e58735e16adaae62..208674673169b7258d9411d818870a640df08b24 100644
index 5b46036868b6c9d082e35591e58735e16adaae62..9563165c945757996da11f55e2221e620dd93327 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -147,6 +147,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
// Paper start - Optimize network
public boolean isPending = true;
+ private io.netty.channel.SingleThreadEventLoop eventLoop; // Leaf - optimise packets that are not flushed
+ private io.netty.channel.SingleThreadEventLoop eventLoop; // Paper - optimise packets that are not flushed
public boolean queueImmunity;
// Paper end - Optimize network
@@ -37,7 +41,7 @@ index 5b46036868b6c9d082e35591e58735e16adaae62..208674673169b7258d9411d818870a64
public void channelActive(ChannelHandlerContext context) throws Exception {
super.channelActive(context);
this.channel = context.channel();
+ this.eventLoop = (io.netty.channel.SingleThreadEventLoop) this.channel.eventLoop(); // Leaf - optimise packets that are not flushed
+ this.eventLoop = (io.netty.channel.SingleThreadEventLoop) this.channel.eventLoop(); // Paper - optimise packets that are not flushed
this.address = this.channel.remoteAddress();
this.preparing = false; // Spigot
if (this.delayedDisconnect != null) {
@@ -45,11 +49,11 @@ index 5b46036868b6c9d082e35591e58735e16adaae62..208674673169b7258d9411d818870a64
if (this.channel.eventLoop().inEventLoop()) {
this.doSendPacket(packet, sendListener, flush);
} else {
+ // Leaf start - optimise packets that are not flushed
+ // Paper start - optimise packets that are not flushed
+ if (!flush && org.dreeam.leaf.config.modules.network.OptimizeNonFlushPacketSending.enabled) {
+ this.eventLoop.lazyExecute(() -> this.doSendPacket(packet, sendListener, flush));
+ } else
+ // Leaf end - optimise packets that are not flushed
+ // Paper end - optimise packets that are not flushed
this.channel.eventLoop().execute(() -> this.doSendPacket(packet, sendListener, flush));
}
}

View File

@@ -612,7 +612,7 @@ index 55572e799b5c8a74a546ac8febc14f80d5731c52..08a06c23c831a4de45b3e537228b8379
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index c2552c3706831f7012b5b449fa43c7d5990056a4..2f9b7627f1801239ee6a89f334a266629b98c989 100644
index c2552c3706831f7012b5b449fa43c7d5990056a4..4e8a1d01a6c0afef92ae56cc4909af06d63e5268 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -961,6 +961,28 @@ public class CraftEventFactory {
@@ -652,7 +652,7 @@ index c2552c3706831f7012b5b449fa43c7d5990056a4..2f9b7627f1801239ee6a89f334a26662
+ // Leaf start - SparklyPaper parallel world ticking mod (collapse original behavior)
+ final BlockPos sourceBlockOverrideRTSnap = getSourceBlockOverrideRT();
+ BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, sourceBlockOverrideRTSnap != null ? sourceBlockOverrideRTSnap : source), state); // SparklyPaper - parallel world ticking
+ // Leaf end
+ // Leaf end - SparklyPaper parallel world ticking mod (collapse original behavior)
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
@@ -666,23 +666,26 @@ index c2552c3706831f7012b5b449fa43c7d5990056a4..2f9b7627f1801239ee6a89f334a26662
return itemStack;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..c4de7346703cbe457dbffed7716a5816314996a1 100644
index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..e7c6b2ab5f2c68f3319ccd52785c8d3488a2eef7 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
@@ -19,11 +19,35 @@ class CraftAsyncTask extends CraftTask {
@@ -19,11 +19,39 @@ class CraftAsyncTask extends CraftTask {
@Override
public boolean isSync() {
+ // Leaf start - Parallel world ticking
+ // Return true if we should run this task synchronously when parallel world ticking is enabled and runAsyncTasksSync is true
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled &&
+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) {
+ return true;
+ }
+ // Leaf end - Parallel world ticking
return false;
}
@Override
public void run() {
+ // Leaf start - Parallel world ticking
+ // If parallel world ticking is enabled and we're configured to run async tasks sync,
+ // execute the task as if it were a sync task (directly on the main thread)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled &&
@@ -700,85 +703,9 @@ index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..c4de7346703cbe457dbffed7716a5816
+ }
+ return;
+ }
+ // Leaf end - Parallel world ticking
+
+ // Original async implementation
final Thread thread = Thread.currentThread();
// Paper start - name threads according to running plugin
final String nameBefore = thread.getName();
@@ -52,49 +76,49 @@ class CraftAsyncTask extends CraftTask {
}
});
}
- Throwable thrown = null;
- try {
- super.run();
- } catch (final Throwable t) {
- thrown = t;
- this.getOwner().getLogger().log(
+ Throwable thrown = null;
+ try {
+ super.run();
+ } catch (final Throwable t) {
+ thrown = t;
+ this.getOwner().getLogger().log(
Level.WARNING,
String.format(
"Plugin %s generated an exception while executing task %s",
this.getOwner().getDescription().getFullName(),
this.getTaskId()),
thrown);
- } finally {
- // Cleanup is important for any async task, otherwise ghost tasks are everywhere
- synchronized (this.workers) {
- try {
- final Iterator<BukkitWorker> workers = this.workers.iterator();
- boolean removed = false;
- while (workers.hasNext()) {
- if (workers.next().getThread() == thread) {
- workers.remove();
- removed = true; // Don't throw exception
- break;
+ } finally {
+ // Cleanup is important for any async task, otherwise ghost tasks are everywhere
+ synchronized (this.workers) {
+ try {
+ final Iterator<BukkitWorker> workers = this.workers.iterator();
+ boolean removed = false;
+ while (workers.hasNext()) {
+ if (workers.next().getThread() == thread) {
+ workers.remove();
+ removed = true; // Don't throw exception
+ break;
+ }
}
- }
- if (!removed) {
- throw new IllegalStateException(
+ if (!removed) {
+ throw new IllegalStateException(
String.format(
"Unable to remove worker %s on task %s for %s",
thread.getName(),
this.getTaskId(),
this.getOwner().getDescription().getFullName()),
thrown); // We don't want to lose the original exception, if any
- }
- } finally {
- if (this.getPeriod() < 0 && this.workers.isEmpty()) {
- // At this spot, we know we are the final async task being executed!
- // Because we have the lock, nothing else is running or will run because delay < 0
- this.runners.remove(this.getTaskId());
+ }
+ } finally {
+ if (this.getPeriod() < 0 && this.workers.isEmpty()) {
+ // At this spot, we know we are the final async task being executed!
+ // Because we have the lock, nothing else is running or will run because delay < 0
+ this.runners.remove(this.getTaskId());
+ }
}
}
}
- }
} finally { thread.setName(nameBefore); } // Paper - name threads according to running plugin
}

View File

@@ -5,24 +5,33 @@ Subject: [PATCH] Async playerdata saving
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index f2d87c12dd19210ce7e2147fada5c10191008632..f64844fb39cd02d8645491a8b482b21643334500 100644
index f2d87c12dd19210ce7e2147fada5c10191008632..0cc6b6c597ca8c955b246764de637be4c25bfb40 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -207,7 +207,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
}
private CompoundTag getData() {
- return this.storage.load(this.profile.getName(), this.profile.getId().toString()).orElse(null);
+ return this.storage.load(this.profile.getName(), this.profile.getId().toString(), this.profile.getId()).orElse(null); // Leaf - Async playerdata saving
}
private CompoundTag getBukkitData() {
@@ -813,6 +813,17 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
* @param compoundTag
*/
private void save(CompoundTag compoundTag) {
+ // Leaf start
+ // Leaf start - Async playerdata saving
+ synchronized (server.console.playerDataStorage) {
+ while (server.console.playerDataStorage.saving.contains(getUniqueId().toString())) {
+ while (server.console.playerDataStorage.savingQueue.contains(getUniqueId())) {
+ try {
+ Thread.sleep(1L);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ server.console.playerDataStorage.saving.add(getUniqueId().toString());
+ server.console.playerDataStorage.savingQueue.add(getUniqueId());
+ }
+ // Leaf end
+ // Leaf end - Async playerdata saving
File playerDir = server.console.playerDataStorage.getPlayerDir();
try {
File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
@@ -30,12 +39,12 @@ index f2d87c12dd19210ce7e2147fada5c10191008632..f64844fb39cd02d8645491a8b482b216
net.minecraft.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath());
} catch (java.io.IOException e) {
e.printStackTrace();
+ // Leaf start
+ // Leaf start - Async playerdata saving
+ } finally {
+ synchronized (server.console.playerDataStorage) {
+ server.console.playerDataStorage.saving.remove(getUniqueId().toString());
+ server.console.playerDataStorage.savingQueue.remove(getUniqueId());
+ }
+ // Leaf end
+ // Leaf end - Async playerdata saving
}
}
// Purpur end - OfflinePlayer API

View File

@@ -8,6 +8,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class AsyncPlayerDataSaving {
public static final ExecutorService IO_POOL = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),

View File

@@ -12,7 +12,6 @@ public class AsyncBlockFinding extends ConfigModules {
@Experimental
public static boolean enabled = false;
public static boolean asyncBlockFindingInitialized;
@Override

View File

@@ -14,22 +14,22 @@ public class OptimizeNonFlushPacketSending extends ConfigModules {
@Override
public void onLoaded() {
enabled = config.getBoolean(getBasePath() + ".OptimizeNonFlushPacketSending", enabled, config.pickStringRegionBased("""
WARNING: This option is NOT compatible with ProtocolLib and may cause
WARNING: This option is NOT compatible with ProtocolLib and may cause
issues with other plugins that modify packet handling.
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.
Requires server restart to take effect.
""",
"""
警告此选项与 ProtocolLib 不兼容并可能导致与其他修改数据包
处理的插件出现问题
警告: 此选项与 ProtocolLib 不兼容, 并可能导致与其他修改数据包
处理的插件出现问题.
通过使用 Netty 的 lazyExecute 方法来优化非刷新数据包的发送
避免在调度数据包操作时进行昂贵的线程唤醒调用
通过使用 Netty 的 lazyExecute 方法来优化非刷新数据包的发送,
避免在调度数据包操作时进行昂贵的线程唤醒调用.
需要重启服务器才能生效
需要重启服务器才能生效.
"""));
}
}