mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-29 11:59:24 +00:00
Update changes from ver/1.21.4 branch
This commit is contained in:
@@ -5,7 +5,7 @@ Subject: [PATCH] Configurable connection message
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index c18921f4ec1d4c205fa1d6efbb60eb05dcec4908..a289c3247ef6e1b7ae76fdc86c286e7b426731b4 100644
|
||||
index c18921f4ec1d4c205fa1d6efbb60eb05dcec4908..2d626735c75caa3ff6d5435882c4303aa204bd1e 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -336,7 +336,7 @@ public abstract class PlayerList {
|
||||
@@ -35,7 +35,7 @@ index c18921f4ec1d4c205fa1d6efbb60eb05dcec4908..a289c3247ef6e1b7ae76fdc86c286e7b
|
||||
this.cserver.getPluginManager().callEvent(playerQuitEvent);
|
||||
player.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
|
||||
|
||||
@@ -1527,4 +1527,34 @@ public abstract class PlayerList {
|
||||
@@ -1527,4 +1527,40 @@ public abstract class PlayerList {
|
||||
public boolean isAllowCommandsForAllPlayers() {
|
||||
return this.allowCommandsForAllPlayers;
|
||||
}
|
||||
@@ -47,7 +47,10 @@ index c18921f4ec1d4c205fa1d6efbb60eb05dcec4908..a289c3247ef6e1b7ae76fdc86c286e7b
|
||||
+ return io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultJoinMsg);
|
||||
+ }
|
||||
+
|
||||
+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.dreeam.leaf.config.modules.misc.ConnectionMessage.joinMessage)
|
||||
+ final String joinMessage = org.dreeam.leaf.config.modules.misc.ConnectionMessage.joinMessage
|
||||
+ .replace("<player_name>", craftPlayer.getName());
|
||||
+
|
||||
+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(joinMessage)
|
||||
+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<player_name>").replacement(craftPlayer.getName()).build())
|
||||
+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<player_displayname>").replacement(craftPlayer.displayName()).build());
|
||||
+ }
|
||||
@@ -61,7 +64,10 @@ index c18921f4ec1d4c205fa1d6efbb60eb05dcec4908..a289c3247ef6e1b7ae76fdc86c286e7b
|
||||
+ return defaultJoinMsg;
|
||||
+ }
|
||||
+
|
||||
+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage)
|
||||
+ final String quitMessage = org.dreeam.leaf.config.modules.misc.ConnectionMessage.quitMessage
|
||||
+ .replace("<player_name>", craftPlayer.getName());
|
||||
+
|
||||
+ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(quitMessage)
|
||||
+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<player_name>").replacement(craftPlayer.getName()).build())
|
||||
+ .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder().matchLiteral("<player_displayname>").replacement(craftPlayer.displayName()).build());
|
||||
+ }
|
||||
|
||||
@@ -199,7 +199,7 @@ index 209a2b6a30d334fc4f6d0b1c02682db7f0b5e435..1489ecc2754901c6f30ec1b5ff0f324b
|
||||
|
||||
attributesToSync.clear();
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 66177932ec65b7c4c2df0c021eeefbdb51f71eea..fc1430cb711a86281cfc7b7c94221e7ef867da9e 100644
|
||||
index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2503,7 +2503,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -211,6 +211,15 @@ index 66177932ec65b7c4c2df0c021eeefbdb51f71eea..fc1430cb711a86281cfc7b7c94221e7e
|
||||
return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system
|
||||
}
|
||||
|
||||
@@ -2739,7 +2739,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
map.carriedByPlayers.remove(player);
|
||||
- if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer.player == player)) {
|
||||
+ if (map.carriedBy.removeIf(holdingPlayer -> holdingPlayer != null && holdingPlayer.player == player)) { // Leaf - Multithreaded tracker
|
||||
map.decorations.remove(player.getName().getString());
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 27ca0be25253b35ebfe54b725f9ba28a120f4ea0..94dd2887398cbebb60200e9a5c61c01b2fda0a7f 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -264,3 +273,15 @@ index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..2b8b335cf5779d1b6eb639935d1b92d8
|
||||
private final AttributeSupplier supplier;
|
||||
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
|
||||
diff --git a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
index 3ae69b17fec1cdb2bee2b5a795026a875f197c30..df471cd42f4084facb895b229c261b685054c3ae 100644
|
||||
--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
@@ -211,6 +211,7 @@ public class MapItemSavedData extends SavedData {
|
||||
|
||||
for (int i = 0; i < this.carriedBy.size(); i++) {
|
||||
MapItemSavedData.HoldingPlayer holdingPlayer1 = this.carriedBy.get(i);
|
||||
+ if (holdingPlayer1 == null) continue; // Leaf - Multithreaded tracker
|
||||
Player player1 = holdingPlayer1.player;
|
||||
String string = player1.getName().getString();
|
||||
if (!player1.isRemoved() && (player1.getInventory().contains(predicate) || mapStack.isFramed())) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
|
||||
Date: Fri, 23 Aug 2024 22:04:20 -0400
|
||||
Subject: [PATCH] Nitori: Async playerdata Save
|
||||
Subject: [PATCH] Nitori: Async playerdata saving
|
||||
|
||||
Original license: GPL v3
|
||||
Original project: https://github.com/Gensokyo-Reimagined/Nitori
|
||||
@@ -24,7 +24,7 @@ index 17d3a8a2cc3c86ed6aae9c20ed9f281dc9715cf5..354823def23167feb1e7b35cd9db5ef1
|
||||
|
||||
public String getLocalIp() {
|
||||
diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java
|
||||
index 8104f71c30c1fa46c83acdf0b2e58483df9d89cc..aec5646db69d972e8c9119e62dad0cece4432ad1 100644
|
||||
index 8104f71c30c1fa46c83acdf0b2e58483df9d89cc..169b4544ab3d0e8515a2d7020a23ae0e2e0c952d 100644
|
||||
--- a/net/minecraft/world/level/storage/LevelStorageSource.java
|
||||
+++ b/net/minecraft/world/level/storage/LevelStorageSource.java
|
||||
@@ -521,15 +521,26 @@ public class LevelStorageSource {
|
||||
@@ -45,7 +45,7 @@ index 8104f71c30c1fa46c83acdf0b2e58483df9d89cc..aec5646db69d972e8c9119e62dad0cec
|
||||
- LevelStorageSource.LOGGER.error("Failed to save level {}", path, var6);
|
||||
+ LevelStorageSource.LOGGER.error("Failed to encode level {}", path, var6);
|
||||
}
|
||||
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.save(() -> {
|
||||
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
|
||||
+ try {
|
||||
+ Path path1 = Files.createTempFile(path, "level", ".dat");
|
||||
+ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
|
||||
@@ -61,25 +61,25 @@ index 8104f71c30c1fa46c83acdf0b2e58483df9d89cc..aec5646db69d972e8c9119e62dad0cec
|
||||
|
||||
public Optional<Path> getIconFile() {
|
||||
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
index ab9282c04c1996b037567d07f95e2b150bcfcd38..fa33d4b56eec6e00912e8027195c6f63c440bc59 100644
|
||||
index ab9282c04c1996b037567d07f95e2b150bcfcd38..7a39ea109dee258e7fb83982572ee18731b5446f 100644
|
||||
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
|
||||
@@ -23,6 +23,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<java.util.UUID> savingQueue = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(); // Leaf - Async playerdata saving
|
||||
+ private final java.util.Map<java.util.UUID, java.util.concurrent.Future<?>> savingLocks = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // Leaf - Async playerdata saving
|
||||
|
||||
public PlayerDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) {
|
||||
this.fixerUpper = fixerUpper;
|
||||
@@ -32,17 +33,43 @@ public class PlayerDataStorage {
|
||||
@@ -32,19 +33,82 @@ public class PlayerDataStorage {
|
||||
|
||||
public void save(Player player) {
|
||||
if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot
|
||||
+ // Leaf start - Async playerdata saving
|
||||
+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
|
||||
+ CompoundTag compoundTag;
|
||||
try {
|
||||
CompoundTag compoundTag = player.saveWithoutId(new CompoundTag());
|
||||
- CompoundTag compoundTag = player.saveWithoutId(new CompoundTag());
|
||||
- Path path = this.playerDir.toPath();
|
||||
- Path path1 = Files.createTempFile(path, player.getStringUUID() + "-", ".dat");
|
||||
- NbtIo.writeCompressed(compoundTag, path1);
|
||||
@@ -88,43 +88,83 @@ index ab9282c04c1996b037567d07f95e2b150bcfcd38..fa33d4b56eec6e00912e8027195c6f63
|
||||
- Util.safeReplaceFile(path2, path1, path3);
|
||||
- } catch (Exception var7) {
|
||||
- LOGGER.warn("Failed to save player data for {}", player.getScoreboardName(), var7); // Paper - Print exception
|
||||
+ NbtIo.writeCompressed(compoundTag, nbtBytes);
|
||||
+ compoundTag = player.saveWithoutId(new CompoundTag());
|
||||
+ } catch (Exception exception) {
|
||||
+ LOGGER.warn("Failed to encode player data for {}", player.getScoreboardName(), exception);
|
||||
+ return;
|
||||
}
|
||||
+ String playerName = player.getScoreboardName();
|
||||
+ String stringUuid = player.getStringUUID();
|
||||
+ java.util.UUID playerUuid = player.getUUID();
|
||||
+ synchronized (PlayerDataStorage.this) {
|
||||
+ while (savingQueue.contains(playerUuid)) {
|
||||
+ try {
|
||||
+ Thread.sleep(1L);
|
||||
+ } catch (InterruptedException ignored) {
|
||||
+ }
|
||||
+ }
|
||||
+ savingQueue.add(playerUuid);
|
||||
+ }
|
||||
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.save(() -> {
|
||||
+ try {
|
||||
+ Path path = this.playerDir.toPath();
|
||||
+ Path path1 = Files.createTempFile(path, stringUuid + "-", ".dat");
|
||||
+ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
|
||||
+ Path path2 = path.resolve(stringUuid + ".dat");
|
||||
+ Path path3 = path.resolve(stringUuid + ".dat_old");
|
||||
+ Util.safeReplaceFile(path2, path1, path3);
|
||||
+ } catch (Exception var7) {
|
||||
+ LOGGER.warn("Failed to save player data for {}", playerName, var7); // Paper - Print exception
|
||||
+ } finally {
|
||||
+ synchronized (PlayerDataStorage.this) {
|
||||
+ savingQueue.remove(playerUuid);
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ save(player.getScoreboardName(), player.getUUID(), player.getStringUUID(), compoundTag);
|
||||
+ // Leaf end - Async playerdata saving
|
||||
}
|
||||
|
||||
+ // Leaf start - Async playerdata saving
|
||||
+ public void save(String playerName, java.util.UUID uniqueId, String stringId, CompoundTag compoundTag) {
|
||||
+ var nbtBytes = new it.unimi.dsi.fastutil.io.FastByteArrayOutputStream(65536);
|
||||
+ try {
|
||||
+ NbtIo.writeCompressed(compoundTag, nbtBytes);
|
||||
+ } catch (Exception exception) {
|
||||
+ LOGGER.warn("Failed to encode player data for {}", stringId, exception);
|
||||
+ }
|
||||
+ lockFor(uniqueId, playerName);
|
||||
+ synchronized (PlayerDataStorage.this) {
|
||||
+ org.dreeam.leaf.async.AsyncPlayerDataSaving.submit(() -> {
|
||||
+ try {
|
||||
+ Path path = this.playerDir.toPath();
|
||||
+ Path path1 = Files.createTempFile(path, stringId + "-", ".dat");
|
||||
+ org.apache.commons.io.FileUtils.writeByteArrayToFile(path1.toFile(), nbtBytes.array, 0, nbtBytes.length, false);
|
||||
+ Path path2 = path.resolve(stringId + ".dat");
|
||||
+ Path path3 = path.resolve(stringId + ".dat_old");
|
||||
+ Util.safeReplaceFile(path2, path1, path3);
|
||||
+ } catch (Exception var7) {
|
||||
+ LOGGER.warn("Failed to save player data for {}", playerName, var7);
|
||||
+ } finally {
|
||||
+ synchronized (PlayerDataStorage.this) {
|
||||
+ savingLocks.remove(uniqueId);
|
||||
+ }
|
||||
+ }
|
||||
+ }).ifPresent(future -> savingLocks.put(uniqueId, future));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void lockFor(java.util.UUID uniqueId, String playerName) {
|
||||
+ java.util.concurrent.Future<?> fut;
|
||||
+ synchronized (this) {
|
||||
+ fut = savingLocks.get(uniqueId);
|
||||
+ }
|
||||
+ if (fut == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ while (true) {
|
||||
+ try {
|
||||
+ fut.get(10_000L, java.util.concurrent.TimeUnit.MILLISECONDS);
|
||||
+ break;
|
||||
+ } catch (InterruptedException ignored) {
|
||||
+ } catch (java.util.concurrent.ExecutionException
|
||||
+ | java.util.concurrent.TimeoutException exception) {
|
||||
+ LOGGER.warn("Failed to save player data for {}", playerName, exception);
|
||||
+
|
||||
+ String threadDump = "";
|
||||
+ var threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean();
|
||||
+ for (var threadInfo : threadMXBean.dumpAllThreads(true, true)) {
|
||||
+ if (threadInfo.getThreadName().equals("Leaf IO Thread")) {
|
||||
+ threadDump = threadInfo.toString();
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ LOGGER.warn(threadDump);
|
||||
+ fut.cancel(true);
|
||||
+ break;
|
||||
+ } finally {
|
||||
+ savingLocks.remove(uniqueId);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaf end - Async playerdata saving
|
||||
+
|
||||
private void backup(String name, String stringUuid, String suffix) { // CraftBukkit
|
||||
@@ -58,7 +85,20 @@ public class PlayerDataStorage {
|
||||
Path path = this.playerDir.toPath();
|
||||
Path path1 = path.resolve(stringUuid + suffix); // CraftBukkit
|
||||
@@ -58,7 +122,13 @@ public class PlayerDataStorage {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,19 +174,12 @@ index ab9282c04c1996b037567d07f95e2b150bcfcd38..fa33d4b56eec6e00912e8027195c6f63
|
||||
+ 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 (savingQueue.contains(playerUuid)) {
|
||||
+ try {
|
||||
+ Thread.sleep(1L);
|
||||
+ } catch (InterruptedException ignored) {
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ lockFor(playerUuid, name);
|
||||
+ // Leaf end - Async playerdata saving
|
||||
File file = new File(this.playerDir, stringUuid + suffix); // CraftBukkit
|
||||
// Spigot start
|
||||
boolean usingWrongFile = false;
|
||||
@@ -89,7 +129,7 @@ public class PlayerDataStorage {
|
||||
@@ -89,7 +159,7 @@ public class PlayerDataStorage {
|
||||
|
||||
public Optional<CompoundTag> load(Player player) {
|
||||
// CraftBukkit start
|
||||
@@ -155,7 +188,7 @@ index ab9282c04c1996b037567d07f95e2b150bcfcd38..fa33d4b56eec6e00912e8027195c6f63
|
||||
if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
|
||||
org.bukkit.craftbukkit.entity.CraftPlayer craftPlayer = serverPlayer.getBukkitEntity();
|
||||
// Only update first played if it is older than the one we have
|
||||
@@ -104,20 +144,25 @@ public class PlayerDataStorage {
|
||||
@@ -104,20 +174,25 @@ public class PlayerDataStorage {
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: "Author: PureGero" <puregero@gmail.com>
|
||||
Date: Thu, 1 Aug 2024 00:43:05 +0900
|
||||
Subject: [PATCH] ShreddedPaper: Don't block main thread in
|
||||
Connection#syncAfterConfigurationChange
|
||||
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 00a82873d226f113278632a53c0faca420dd67d4..5b46036868b6c9d082e35591e58735e16adaae62 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -325,6 +325,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
private static void syncAfterConfigurationChange(ChannelFuture future) {
|
||||
try {
|
||||
+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) net.minecraft.server.MinecraftServer.getServer().managedBlock(future::isDone); // ShreddedPaper - Don't block main thread in Connection#syncAfterConfigurationChange
|
||||
future.syncUninterruptibly();
|
||||
} catch (Exception var2) {
|
||||
if (var2 instanceof ClosedChannelException) {
|
||||
@@ -159,7 +159,7 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..bed3a64388bb43e47c2ba4e67f7dde5b
|
||||
|
||||
public static final class SaveState {
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
index 963c51d14f87d2557a3d686fb8fe3ec9cba367b3..492618b13ecc7ba541339fea2f4ea4daddfa143f 100644
|
||||
index 36c033b0ee63dfc273d721fb4b614733e8fdef19..dc9f1bc6dd8b1057da3416e24f15f2329658f996 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||
@@ -24,6 +24,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
||||
@@ -9,27 +9,37 @@ Leaf: ~48ms (-36%)
|
||||
This should help drastically on the farms that use actively changing fluids.
|
||||
|
||||
diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java
|
||||
index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..8646ccf77f738d7b66efd0a7ecf2519d8a8d6bdf 100644
|
||||
index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..c0f78556d112e59333ace60f1522e7fd1efe71c3 100644
|
||||
--- a/net/minecraft/world/level/material/FlowingFluid.java
|
||||
+++ b/net/minecraft/world/level/material/FlowingFluid.java
|
||||
@@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
+import java.util.Queue;
|
||||
+
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -342,31 +344,72 @@ public abstract class FlowingFluid extends Fluid {
|
||||
@@ -342,32 +342,81 @@ public abstract class FlowingFluid extends Fluid {
|
||||
|
||||
protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state);
|
||||
|
||||
- protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) {
|
||||
- int i = 1000;
|
||||
+ // Leaf start - Use BFS on getSlopeDistance
|
||||
+ protected int getSlopeDistance(LevelReader level, BlockPos startPos, int initialDepth, Direction excludedDirection, BlockState startState, FlowingFluid.SpreadContext spreadContext) {
|
||||
+ it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512); // Pre-allocate capacity
|
||||
+ java.util.Queue<FlowingFluid.SlopeDistanceNode> queue = new java.util.ArrayDeque<>(64); // Optimized initial capacity
|
||||
+ it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512);
|
||||
+ java.util.Queue<FlowingFluid.SlopeDistanceNode> queue = new java.util.ArrayDeque<>(256);
|
||||
+
|
||||
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
|
||||
+ if (dir == excludedDirection) continue;
|
||||
+
|
||||
+ BlockPos neighborPos = startPos.relative(dir);
|
||||
+ BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos);
|
||||
+ if (neighborState == null) continue;
|
||||
+
|
||||
+ // Check if the fluid can actually pass through to this first neighbor before adding
|
||||
+ FluidState neighborFluidState = neighborState.getFluidState();
|
||||
+ if (!this.canPassThrough(level, this.getFlowing(), startPos, startState, dir, neighborPos, neighborState, neighborFluidState)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ long visitKey = encodeSlopeNode(neighborPos, dir.getOpposite());
|
||||
+ if (visited.add(visitKey)) {
|
||||
+ queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth, dir.getOpposite(), neighborState));
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- for (Direction direction1 : Direction.Plane.HORIZONTAL) {
|
||||
- if (direction1 != direction) {
|
||||
@@ -41,8 +51,8 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..8646ccf77f738d7b66efd0a7ecf2519d
|
||||
- if (spreadContext.isHole(blockPos)) {
|
||||
- return depth;
|
||||
- }
|
||||
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
|
||||
+ if (dir == excludedDirection) continue;
|
||||
+ int slopeFindDistance = this.getSlopeFindDistance(level);
|
||||
+ int minDistance = 1000;
|
||||
|
||||
- if (depth < this.getSlopeFindDistance(level)) {
|
||||
- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext);
|
||||
@@ -50,27 +60,15 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..8646ccf77f738d7b66efd0a7ecf2519d
|
||||
- i = slopeDistance;
|
||||
- }
|
||||
- }
|
||||
+ BlockPos neighborPos = startPos.relative(dir);
|
||||
+ BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos);
|
||||
+ if (neighborState == null) continue;
|
||||
+ long visitKey = encodeSlopeNode(neighborPos, dir.getOpposite());
|
||||
+ if (visited.add(visitKey)) {
|
||||
+ queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth + 1, dir.getOpposite(), neighborState));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ int slopeFindDistance = this.getSlopeFindDistance(level);
|
||||
+ int minDistance = 1000;
|
||||
+
|
||||
+ // Process the queue
|
||||
+ while (!queue.isEmpty()) {
|
||||
+ FlowingFluid.SlopeDistanceNode current = queue.poll();
|
||||
+ if (current.depth >= slopeFindDistance) continue;
|
||||
+
|
||||
+ if (spreadContext.isHole(current.pos)) {
|
||||
+ return current.depth;
|
||||
+ }
|
||||
+
|
||||
+ if (current.depth >= slopeFindDistance) continue;
|
||||
+
|
||||
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
|
||||
+ if (dir == current.excludedDir) continue;
|
||||
+
|
||||
@@ -91,7 +89,7 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..8646ccf77f738d7b66efd0a7ecf2519d
|
||||
}
|
||||
|
||||
- return i;
|
||||
+ return minDistance; // Return fallback value
|
||||
+ return minDistance;
|
||||
+ }
|
||||
+
|
||||
+ private static long encodeSlopeNode(BlockPos pos, Direction excludedDir) {
|
||||
@@ -111,5 +109,7 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..8646ccf77f738d7b66efd0a7ecf2519d
|
||||
+ this.state = state;
|
||||
+ }
|
||||
}
|
||||
+ // Leaf end - Use BFS on getSlopeDistance
|
||||
|
||||
boolean isWaterHole(BlockGetter level, BlockPos pos, BlockState state, BlockPos belowPos, BlockState belowState) {
|
||||
return canPassThroughWall(Direction.DOWN, level, pos, state, belowPos, belowState)
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Micro optimizations for random tick
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index d8390cb3901a40b97e99990d9f71f12c74f96607..4d7cf866452db7388ab90f9be284e859b39d8e61 100644
|
||||
index 7ca4fd418599cdb1bb1de44f4c3c57f1770a4038..461b620b703c9fca2691f724a9b865ad54aa90a4 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -903,7 +903,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -0,0 +1,183 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +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..2bc0f19b86067491f33f647d2e387acd83492844 100644
|
||||
--- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
+++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
@@ -20,6 +20,19 @@ 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;
|
||||
+ private boolean searchComplete = false;
|
||||
+ // Leaf end - Async Block Finding
|
||||
+
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) {
|
||||
this(mob, speedModifier, searchRange, 1);
|
||||
}
|
||||
@@ -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 (!org.dreeam.leaf.config.modules.async.AsyncBlockFinding.enabled) {
|
||||
+ return findNearestBlockSync();
|
||||
+ }
|
||||
+
|
||||
+ // Check if we're done with the current search
|
||||
+ if (searchComplete) {
|
||||
+ searchComplete = false;
|
||||
+ asyncSearchInProgress = false;
|
||||
+ candidateBlocks.clear();
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ 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;
|
||||
+ searchComplete = true;
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // If no candidates are left and async search is done
|
||||
+ if (candidateBlocks.isEmpty() && !asyncSearchInProgress) {
|
||||
+ searchComplete = true;
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Start async search if needed
|
||||
+ if (!asyncSearchInProgress && candidateBlocks.isEmpty()) {
|
||||
+ asyncSearchInProgress = true;
|
||||
+
|
||||
+ // Get necessary data from main thread
|
||||
+ 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();
|
||||
+ } finally {
|
||||
+ asyncSearchInProgress = false;
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // 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
|
||||
+ 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());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Sort by distance to center (closest first)
|
||||
+ positions.sort((p1, p2) -> {
|
||||
+ double d1 = p1.distSqr(center);
|
||||
+ double d2 = p2.distSqr(center);
|
||||
+ return Double.compare(d1, d2);
|
||||
+ });
|
||||
+
|
||||
+ // Add to candidate queue
|
||||
+ for (BlockPos pos : positions) {
|
||||
+ candidateBlocks.add(pos);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 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();
|
||||
@@ -26,7 +26,7 @@ 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..9563165c945757996da11f55e2221e620dd93327 100644
|
||||
index 00a82873d226f113278632a53c0faca420dd67d4..f3e9de8716f5e1a72ec465ee897c8f0413f7b1c3 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -147,6 +147,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -45,7 +45,7 @@ index 5b46036868b6c9d082e35591e58735e16adaae62..9563165c945757996da11f55e2221e62
|
||||
this.address = this.channel.remoteAddress();
|
||||
this.preparing = false; // Spigot
|
||||
if (this.delayedDisconnect != null) {
|
||||
@@ -477,6 +479,11 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -476,6 +478,11 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
if (this.channel.eventLoop().inEventLoop()) {
|
||||
this.doSendPacket(packet, sendListener, flush);
|
||||
} else {
|
||||
@@ -0,0 +1,319 @@
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Wed, 2 Apr 2025 23:03:22 +0200
|
||||
Subject: [PATCH] Null handling on MultifaceSpreader
|
||||
|
||||
WHYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/MultifaceSpreader.java b/net/minecraft/world/level/block/MultifaceSpreader.java
|
||||
index 5eb291396a83b8d98294c5f53d2e1f4915a0d84e..b5a78dc342bd2d2eecaaa3ab7cc89e391a91b2da 100644
|
||||
--- a/net/minecraft/world/level/block/MultifaceSpreader.java
|
||||
+++ b/net/minecraft/world/level/block/MultifaceSpreader.java
|
||||
@@ -148,6 +148,14 @@ public class MultifaceSpreader {
|
||||
}
|
||||
|
||||
default boolean placeBlock(LevelAccessor level, MultifaceSpreader.SpreadPos pos, BlockState state, boolean markForPostprocessing) {
|
||||
+ // Leaf start - Null handling on MultifaceSpreader
|
||||
+ // Check for null
|
||||
+ if (pos.source() == null || pos.pos() == null) {
|
||||
+ org.dreeam.leaf.config.LeafConfig.LOGGER.warn("Invalid SpreadPos with null source or position: {}", pos);
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Leaf end - Null handling on MultifaceSpreader
|
||||
+
|
||||
BlockState stateForPlacement = this.getStateForPlacement(state, level, pos.pos(), pos.face());
|
||||
if (stateForPlacement != null) {
|
||||
if (markForPostprocessing) {
|
||||
@@ -0,0 +1,112 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taiyou06 <kaandindar21@gmail.com>
|
||||
Date: Sun, 6 Apr 2025 11:22:35 +0200
|
||||
Subject: [PATCH] More virtual threads
|
||||
|
||||
|
||||
diff --git a/net/minecraft/Util.java b/net/minecraft/Util.java
|
||||
index 9918572306e983281d05c6d28c8a5d843348ad2d..1d4ad1370ca041753ce765b1a2feddae59f1f8ca 100644
|
||||
--- a/net/minecraft/Util.java
|
||||
+++ b/net/minecraft/Util.java
|
||||
@@ -98,7 +98,8 @@ public class Util {
|
||||
public static final TracingExecutor DIMENSION_DATA_IO_POOL = makeExtraIoExecutor("Dimension-Data-IO-Worker-"); // Paper - Separate dimension data IO pool
|
||||
private static final TracingExecutor DOWNLOAD_POOL = makeIoExecutor("Download-", true);
|
||||
// Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
|
||||
- public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() {
|
||||
+ // Leaf start - More virtual threads
|
||||
+ public static final ExecutorService PROFILE_EXECUTOR = createProfileExecutor(); /* new Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() {
|
||||
|
||||
private final AtomicInteger count = new AtomicInteger();
|
||||
|
||||
@@ -111,7 +112,30 @@ public class Util {
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
- });
|
||||
+ }); */
|
||||
+
|
||||
+ private static ExecutorService createProfileExecutor() {
|
||||
+ final java.util.concurrent.ThreadFactory factory;
|
||||
+ if (org.dreeam.leaf.config.modules.opt.VT4ProfileExecutor.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported()) {
|
||||
+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory();
|
||||
+ } else {
|
||||
+ factory = new java.util.concurrent.ThreadFactory() {
|
||||
+ private final AtomicInteger count = new AtomicInteger();
|
||||
+
|
||||
+ @Override
|
||||
+ public Thread newThread(Runnable run) {
|
||||
+ Thread ret = new Thread(run);
|
||||
+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement());
|
||||
+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
|
||||
+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
|
||||
+ });
|
||||
+ return ret;
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+ return Executors.newFixedThreadPool(2, factory);
|
||||
+ }
|
||||
+ // Leaf end - More virtual threads
|
||||
// Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
|
||||
private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
|
||||
public static final int LINEAR_LOOKUP_THRESHOLD = 8;
|
||||
@@ -255,16 +279,31 @@ public class Util {
|
||||
}
|
||||
|
||||
private static TracingExecutor makeIoExecutor(String name, boolean daemon) {
|
||||
- AtomicInteger atomicInteger = new AtomicInteger(1);
|
||||
- return new TracingExecutor(Executors.newCachedThreadPool(task -> {
|
||||
- Thread thread = new Thread(task);
|
||||
- String string = name + atomicInteger.getAndIncrement();
|
||||
- TracyClient.setThreadName(string, name.hashCode());
|
||||
- thread.setName(string);
|
||||
- thread.setDaemon(daemon);
|
||||
- thread.setUncaughtExceptionHandler(Util::onThreadException);
|
||||
- return thread;
|
||||
- }));
|
||||
+ // Leaf start - More virtual threads
|
||||
+ final java.util.concurrent.ThreadFactory factory;
|
||||
+ final boolean useVirtualThreads; // Gale - virtual thread support
|
||||
+ if (name.startsWith("Download-")) { // Gale - virtual thread support
|
||||
+ useVirtualThreads = org.dreeam.leaf.config.modules.opt.VT4DownloadPool.enabled && org.galemc.gale.virtualthread.VirtualThreadService.isSupported(); // Gale - virtual thread support
|
||||
+ } else {
|
||||
+ useVirtualThreads = false;
|
||||
+ }
|
||||
+
|
||||
+ if (useVirtualThreads) {
|
||||
+ factory = org.galemc.gale.virtualthread.VirtualThreadService.get().createFactory();
|
||||
+ } else {
|
||||
+ AtomicInteger atomicInteger = new AtomicInteger(1);
|
||||
+ factory = task -> {
|
||||
+ Thread thread = new Thread(task);
|
||||
+ String string = name + atomicInteger.getAndIncrement();
|
||||
+ TracyClient.setThreadName(string, name.hashCode());
|
||||
+ thread.setName(string);
|
||||
+ thread.setDaemon(daemon);
|
||||
+ thread.setUncaughtExceptionHandler(Util::onThreadException);
|
||||
+ return thread;
|
||||
+ };
|
||||
+ }
|
||||
+ return new TracingExecutor(Executors.newCachedThreadPool(factory));
|
||||
+ // Leaf end - More virtual threads
|
||||
}
|
||||
|
||||
// Paper start - Separate dimension data IO pool
|
||||
@@ -1109,7 +1148,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public static <T> Typed<T> readTypedOrThrow(Type<T> type, Dynamic<?> data, boolean partial) {
|
||||
- DataResult<Typed<T>> dataResult = type.readTyped(data).map(Pair::getFirst);
|
||||
+ DataResult<Typed<T>> dataResult = type.readTyped(data).map(Pair::getFirst); // Paper - Fix generics issue // Gale - Fix generics issue
|
||||
|
||||
try {
|
||||
return partial ? dataResult.getPartialOrThrow(IllegalStateException::new) : dataResult.getOrThrow(IllegalStateException::new);
|
||||
@@ -1158,7 +1197,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public void openUri(URI uri) {
|
||||
- throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code
|
||||
+ // throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code // Leaf - More virtual threads - This method is not useful on dedicated servers
|
||||
}
|
||||
|
||||
public void openFile(File file) {
|
||||
@@ -5,17 +5,18 @@ 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
|
||||
index 19180c08f41db939c1a9f0caeb62e5beb1117f69..59ab5bd3582cdae351d579719244c4ad28878a00 100644
|
||||
index 19180c08f41db939c1a9f0caeb62e5beb1117f69..c765118189cbf8db7291c1d50214a7f31acc3a49 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
@@ -340,6 +340,15 @@ public class CraftInventory implements Inventory {
|
||||
@@ -340,6 +340,16 @@ public class CraftInventory implements Inventory {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Leaf start - PlayerInventoryOverflowEvent
|
||||
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0 && !leftover.isEmpty() && this.getHolder() instanceof org.bukkit.craftbukkit.entity.CraftPlayer craftPlayer) {
|
||||
+ new org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent(craftPlayer, leftover).callEvent();
|
||||
+ if (org.dreeam.leaf.event.player.PlayerInventoryOverflowEvent.getHandlerList().getRegisteredListeners().length > 0
|
||||
+ && !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();
|
||||
+
|
||||
+ leftover = new HashMap<>();
|
||||
+ }
|
||||
|
||||
@@ -5,7 +5,7 @@ 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 1456f2d1a92c8315177fb03d0c7ec943d5f5b097..e70692272aae39ea01fb6860ec4cb703ea531781 100644
|
||||
index 1456f2d1a92c8315177fb03d0c7ec943d5f5b097..aadc92b9e82bfe3d65ea8f47ac28ba2d70eb3a7f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
|
||||
@@ -199,7 +199,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
||||
@@ -17,34 +17,21 @@ index 1456f2d1a92c8315177fb03d0c7ec943d5f5b097..e70692272aae39ea01fb6860ec4cb703
|
||||
}
|
||||
|
||||
private CompoundTag getBukkitData() {
|
||||
@@ -744,6 +744,17 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
||||
@@ -744,16 +744,7 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
||||
* @param compoundTag
|
||||
*/
|
||||
private void save(CompoundTag compoundTag) {
|
||||
+ // Leaf start - Async playerdata saving
|
||||
+ synchronized (server.console.playerDataStorage) {
|
||||
+ while (server.console.playerDataStorage.savingQueue.contains(getUniqueId())) {
|
||||
+ try {
|
||||
+ Thread.sleep(1L);
|
||||
+ } catch (InterruptedException ignored) {
|
||||
+ }
|
||||
+ }
|
||||
+ server.console.playerDataStorage.savingQueue.add(getUniqueId());
|
||||
+ }
|
||||
+ // Leaf end - Async playerdata saving
|
||||
File playerDir = server.console.playerDataStorage.getPlayerDir();
|
||||
try {
|
||||
File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
|
||||
@@ -753,6 +764,12 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
|
||||
net.minecraft.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath());
|
||||
} catch (java.io.IOException e) {
|
||||
e.printStackTrace();
|
||||
+ // Leaf start - Async playerdata saving
|
||||
+ } finally {
|
||||
+ synchronized (server.console.playerDataStorage) {
|
||||
+ server.console.playerDataStorage.savingQueue.remove(getUniqueId());
|
||||
+ }
|
||||
+ // Leaf end - Async playerdata saving
|
||||
}
|
||||
- File playerDir = server.console.playerDataStorage.getPlayerDir();
|
||||
- try {
|
||||
- File tempFile = File.createTempFile(this.getUniqueId()+"-", ".dat", playerDir);
|
||||
- net.minecraft.nbt.NbtIo.writeCompressed(compoundTag, tempFile.toPath());
|
||||
- File playerDataFile = new File(playerDir, this.getUniqueId()+".dat");
|
||||
- File playerDataFileOld = new File(playerDir, this.getUniqueId()+".dat_old");
|
||||
- net.minecraft.Util.safeReplaceFile(playerDataFile.toPath(), tempFile.toPath(), playerDataFileOld.toPath());
|
||||
- } catch (java.io.IOException e) {
|
||||
- e.printStackTrace();
|
||||
- }
|
||||
+ server.console.playerDataStorage.save(this.getName(), this.getUniqueId(), this.getUniqueId().toString(), compoundTag); // Leaf - Async playerdata saving
|
||||
}
|
||||
// Purpur end - OfflinePlayer API
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package org.dreeam.leaf.async;
|
||||
|
||||
import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -10,7 +12,7 @@ import java.util.concurrent.TimeUnit;
|
||||
public class AsyncPlayerDataSaving {
|
||||
|
||||
public static final ExecutorService IO_POOL = new ThreadPoolExecutor(
|
||||
1, 1, 0, TimeUnit.MILLISECONDS,
|
||||
1, 1, 0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new com.google.common.util.concurrent.ThreadFactoryBuilder()
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
@@ -23,11 +25,12 @@ public class AsyncPlayerDataSaving {
|
||||
private AsyncPlayerDataSaving() {
|
||||
}
|
||||
|
||||
public static void save(Runnable runnable) {
|
||||
public static Optional<Future<?>> submit(Runnable runnable) {
|
||||
if (!AsyncPlayerDataSave.enabled) {
|
||||
runnable.run();
|
||||
return Optional.empty();
|
||||
} else {
|
||||
IO_POOL.execute(runnable);
|
||||
return Optional.of(IO_POOL.submit(runnable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class AsyncBlockFinding extends ConfigModules {
|
||||
|
||||
@@ -10,14 +9,12 @@ public class AsyncBlockFinding extends ConfigModules {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-block-finding";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
public static boolean asyncBlockFindingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
**Experimental feature**
|
||||
This moves the expensive search calculations to a background thread while
|
||||
keeping the actual block validation on the main thread.""",
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class AsyncChunkSend extends ConfigModules {
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class AsyncTargetFinding extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".async-target-finding";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
public static boolean asyncTargetFindingInitialized;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
config.addCommentRegionBased(getBasePath(), """
|
||||
**Experimental feature**
|
||||
This moves the expensive entity target search calculations to a background thread while
|
||||
keeping the actual entity validation on the main thread.""",
|
||||
"""
|
||||
这会将昂贵的实体目标搜索计算移至后台线程, 同时在主线程上保持实际的实体验证.""");
|
||||
|
||||
if (!asyncTargetFindingInitialized) {
|
||||
asyncTargetFindingInitialized = true;
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class VT4DownloadPool extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-download-pool", enabled,
|
||||
config.pickStringRegionBased(
|
||||
"Use the new Virtual Thread introduced in JDK 21 for download worker pool.",
|
||||
"是否为下载工作线程池使用虚拟线程(如果可用)。"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class VT4ProfileExecutor extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-profile-executor", enabled,
|
||||
config.pickStringRegionBased(
|
||||
"Use the new Virtual Thread introduced in JDK 21 for profile lookup executor.",
|
||||
"是否为档案查询执行器使用虚拟线程(如果可用)。"));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.util.function.Function;
|
||||
|
||||
public class StringCanonizingOpenHashMap<T> extends Object2ObjectOpenHashMap<String, T> {
|
||||
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newWeakInterner();
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newBuilder().weak().concurrencyLevel(16).<String>build();
|
||||
|
||||
private static String intern(String key) {
|
||||
return key != null ? KEY_INTERNER.intern(key) : null;
|
||||
@@ -36,9 +36,10 @@ public class StringCanonizingOpenHashMap<T> extends Object2ObjectOpenHashMap<Str
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends T> m) {
|
||||
if (m.isEmpty()) return;
|
||||
Map<String, T> tmp = new Object2ObjectOpenHashMap<>(m.size());
|
||||
m.forEach((k, v) -> tmp.put(intern(k), v));
|
||||
super.putAll(tmp);
|
||||
ensureCapacity(size() + m.size());
|
||||
for (Map.Entry<? extends String, ? extends T> entry : m.entrySet()) {
|
||||
super.put(intern(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void putWithoutInterning(String key, T value) {
|
||||
@@ -46,7 +47,7 @@ public class StringCanonizingOpenHashMap<T> extends Object2ObjectOpenHashMap<Str
|
||||
}
|
||||
|
||||
public static <T> StringCanonizingOpenHashMap<T> deepCopy(StringCanonizingOpenHashMap<T> incomingMap, Function<T, T> deepCopier) {
|
||||
StringCanonizingOpenHashMap<T> newMap = new StringCanonizingOpenHashMap<>(incomingMap.size(), 0.8f);
|
||||
StringCanonizingOpenHashMap<T> newMap = new StringCanonizingOpenHashMap<>(incomingMap.size(), incomingMap.f);
|
||||
ObjectIterator<Entry<String, T>> iterator = incomingMap.object2ObjectEntrySet().fastIterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
Reference in New Issue
Block a user