9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-23 17:09:29 +00:00

Merge branch 'ver/1.21.4' into dev/1.21.5

This commit is contained in:
Dreeam
2025-06-03 05:53:33 +08:00
56 changed files with 3006 additions and 1421 deletions

View File

@@ -17,6 +17,7 @@ public net.minecraft.world.entity.decoration.ArmorStand noTickEquipmentDirty
public net.minecraft.world.entity.monster.Shulker MAX_SCALE
public net.minecraft.world.entity.player.Player canGlide()Z
public net.minecraft.world.item.CrossbowItem getShotPitch(Lnet/minecraft/util/RandomSource;I)F
public net.minecraft.world.level.block.PoweredRailBlock findPoweredRailSignal(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;ZI)Z
public net.minecraft.world.level.block.entity.FuelValues values
public net.minecraft.world.level.chunk.PaletteResize
public net.minecraft.world.level.chunk.storage.RegionFile getOversizedData(II)Lnet/minecraft/nbt/CompoundTag;

View File

@@ -35,7 +35,7 @@ subprojects {
options.release = 21
options.isFork = true
options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal"))
options.forkOptions.memoryMaximumSize = "6g" // Prevent OOM during building
options.forkOptions.memoryMaximumSize = "2g" // Prevent OOM during building
}
tasks.withType<Javadoc> {
options.encoding = Charsets.UTF_8.name()

View File

@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Date: Wed, 31 Jul 2024 22:05:21 +0800
Subject: [PATCH] Leaf Bootstrap
Removed since Leaf 1.21.4, useless
org.bukkit.craftbukkit.Main#main -> LeafBootstrap -> PaperBootstrap -> ...
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index ecb0fcd1f3b3f3d7751eded3cdf0977c1889c9ed..d0becb56a9911ef4cc55ae8d7c47832f442ad52f 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -278,7 +278,8 @@ public class Main {
System.setProperty("jdk.console", "java.base"); // Paper - revert default console provider back to java.base so we can have our own jline
//System.out.println("Loading libraries, please wait...");
//net.minecraft.server.Main.main(options);
- io.papermc.paper.PaperBootstrap.boot(options);
+ //io.papermc.paper.PaperBootstrap.boot(options); // Leaf - Leaf Boostrap - diff on change
+ org.dreeam.leaf.LeafBootstrap.boot(options); // Leaf - Leaf Boostrap
} catch (Throwable t) {
t.printStackTrace();
}

View File

@@ -4,6 +4,7 @@ Date: Thu, 1 Aug 2024 00:43:05 +0900
Subject: [PATCH] ShreddedPaper: Don't block main thread in
Connection#syncAfterConfigurationChange
Removed since Leaf 1.21.4, replaced by async config switch
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 00a82873d226f113278632a53c0faca420dd67d4..5b46036868b6c9d082e35591e58735e16adaae62 100644

View File

@@ -3,6 +3,8 @@ From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 5 Sep 2024 15:42:15 -0700
Subject: [PATCH] Moonrise: Optimise checkInsideBlocks
Removed since Leaf 1.21.3, Optimized in Minecraft
Original license: GPLv3
Original project: https://github.com/Tuinity/Moonrise

View File

@@ -3,6 +3,8 @@ From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 5 Sep 2024 16:23:04 -0700
Subject: [PATCH] Moonrise: Avoid streams for block retrieval in Entity#move
Removed since Leaf 1.21.3, Optimized in Minecraft
Original license: GPLv3
Original project: https://github.com/Tuinity/Moonrise

View File

@@ -3,6 +3,7 @@ From: Taiyou06 <kaandindar21@gmail.com>
Date: Fri, 8 Nov 2024 00:54:42 +0100
Subject: [PATCH] Use MCUtil.asyncExecutor for MAIN_WORKER_EXECUTOR
Removed since Leaf 1.21.3
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 815253d03b85a7a476c1efdeca9496fd64afc137..bb4c9bbebaefe9a0c7d213e9b2b07308e684dc7c 100644

View File

@@ -105,7 +105,7 @@ index dddbb18992348fb7e8a6552423d134809cd7fdbc..0e6e71030e3fd1335fff796b861524a4
if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index 792ba93b531e9586e26aafa00830022a8996fc04..e4ea26ae84efde7ce54e08a246a6ea2ae2a17151 100644
index abccabb8a0a1a9730b7df070dd25f3ca215af362..0a4bcc4c44fed2ededafaf0641315e072b7ba771 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -168,6 +168,11 @@ public class PlayerAdvancements {
@@ -134,7 +134,7 @@ index 5c0a04db38821dbb0cba2bb6f0787f113d167efd..cd153db93f709c3142942fac88ae3ca2
.filter(player -> !playerList.isOp(player.getGameProfile()))
.map(player -> player.getGameProfile().getName()),
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235eff774133 100644
index b49dd636e730f0c5b609df68ee51bcd12efc1eaa..5e971bca365c692d4ce0c58693592002ce01471c 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -216,6 +216,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -153,7 +153,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e
}
// Paper start
@@ -2672,6 +2674,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2698,6 +2700,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
@@ -165,7 +165,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e
ServerLevel.this.updateSleepingPlayerList();
}
@@ -2742,6 +2749,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2768,6 +2775,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
@@ -178,7 +178,7 @@ index 94ee31a4a02edb003b98a09b0311355c1db4f547..f8bd39ddd7b6948734254acfb8b0235e
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 622257dbbe572de33e15abef9055016268730261..dfb4524d80f642eff1b146dd2fbfa07f21d844c6 100644
index d44c3baa2ef30d5cd4c46e491ff9198fa558513c..f89d28595fa9ca12e414f7b3cc86085ff0769e72 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -195,7 +195,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -191,7 +191,7 @@ index 622257dbbe572de33e15abef9055016268730261..dfb4524d80f642eff1b146dd2fbfa07f
private final ServerStatsCounter stats;
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index e3d09d5f4efb32bb276e001e5ee747a775b502ee..78b15d750d75e5d4c2318a3a18e83afdd5f4fbe1 100644
index c26bf04abe86b566e7f5cd29191a0a853f9808f8..4c172e2aee3e48d42009cd39b28f694aa71e20e3 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -132,6 +132,7 @@ public abstract class PlayerList {

View File

@@ -265,7 +265,7 @@ index 5ab2c8333178335515e619b87ae420f948c83bd1..be3b2f023897a8823560ee059cb16ec9
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11b57ae175 100644
index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..7f791ff5bdf6c29c78f863d21af16270a74d8e7e 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,20 @@ index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11
}
@Override
@@ -1677,6 +1690,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -923,6 +936,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
serverLevel.save(null, flush, serverLevel.noSave && !forced, close); // Paper - add close param
+ // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads
+ // Only prepare shutdown if 'close' is true, indicating this save is part of server shutdown
+ if (close && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ serverLevel.prepareShutdown();
+ }
+ // Leaf end - SparklyPaper - parallel world ticking - Shutdown handling for async reads
flag = true;
}
@@ -1677,6 +1696,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -345,7 +358,7 @@ index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11
protected void tickChildren(BooleanSupplier hasTimeLeft) {
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
@@ -1743,28 +1768,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1743,28 +1774,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
@@ -414,7 +427,7 @@ index c50a301a0c2365c2052aefc6a23fcf6fa82e1b9d..ac751d460ae0c8dbb858c4047c459a11
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
this.tickConnection();
@@ -1844,6 +1891,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1844,6 +1897,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());
@@ -568,7 +581,7 @@ index d4048661575ebfaf128ba25da365843774364e0e..33dd16a26edd2974f04d9a868d3e58e8
// Gale start - Pufferfish - SIMD support
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d08e18122 100644
index a66e5f6652d9633c856490de36d8d8fdf8a5298a..d6524d5c442555eaeb4d90f6a101262ee669f0d9 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -182,7 +182,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -580,23 +593,173 @@ index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d
// Paper - rewrite chunk system
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
@@ -208,6 +208,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -208,7 +208,9 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private double preciseTime; // Purpur - Configurable daylight cycle
private boolean forceTime; // Purpur - Configurable daylight cycle
private final RandomSequences randomSequences;
-
+ public java.util.concurrent.ExecutorService tickExecutor; // SparklyPaper - parallel world ticking
+ public final java.util.concurrent.ConcurrentLinkedQueue<org.dreeam.leaf.async.world.WorldReadRequest> asyncReadRequestQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Leaf - SparklyPaper - parallel world ticking
+ private volatile boolean isShuttingDown = false; // Leaf - SparklyPaper - parallel world ticking - Shutdown handling for async reads
// CraftBukkit start
public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
@@ -703,6 +704,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public final UUID uuid;
@@ -703,8 +705,26 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle
this.realPlayers = Lists.newArrayList(); // Leaves - skip
+ this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.dreeam.leaf.async.world.SparklyPaperServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking
+ }
+
+ // Leaf start - SparklyPaper - parallel world ticking - Shutdown handling for async reads
+ public boolean isShuttingDown() {
+ return this.isShuttingDown;
}
+ public void prepareShutdown() {
+ this.isShuttingDown = true;
+ org.dreeam.leaf.async.world.WorldReadRequest req;
+ int clearedRequests = 0;
+ while ((req = this.asyncReadRequestQueue.poll()) != null) {
+ req.future().completeExceptionally(new IllegalStateException("World " + this.getWorld().getName() + " is shutting down. Cannot process buffered read: " + req.type()));
+ clearedRequests++;
+ }
+ if (clearedRequests > 0) MinecraftServer.LOGGER.info("PWT: Cleared " + clearedRequests + " pending async read requests for world " + this.getWorld().getName() + " during shutdown.");
+ }
+ // Leaf end - SparklyPaper - parallel world ticking - Shutdown handling for async reads
+
// Paper start
@@ -1313,9 +1315,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@Override
public boolean hasChunk(int chunkX, int chunkZ) {
@@ -737,8 +757,112 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public Player[] eligibleDespawnCheckingPlayerCache = new Player[0]; // Leaf - Cache eligible players for despawn checks
+ // Leaf start - SparklyPaper - parallel world ticking
+ private void processAsyncReadRequests() {
+ // Only process if parallel ticking is enabled and buffering is active
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ||
+ !org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.asyncUnsafeReadHandling.equals("BUFFERED")) {
+ // Clear queue if buffering gets disabled to prevent memory leaks
+ if (!this.asyncReadRequestQueue.isEmpty()) {
+ org.dreeam.leaf.async.world.WorldReadRequest req;
+ while ((req = this.asyncReadRequestQueue.poll()) != null) {
+ req.future().completeExceptionally(new IllegalStateException("Async read buffering disabled while request was pending."));
+ }
+ }
+ return;
+ }
+
+ org.dreeam.leaf.async.world.WorldReadRequest request;
+ int processed = 0;
+ // Limit processing per tick to avoid stalling the tick loop
+ int maxToProcess = 16384; // Consider making this configurable
+
+ while (processed < maxToProcess && (request = this.asyncReadRequestQueue.poll()) != null) {
+ processed++;
+ // Ensure we are on the correct thread before executing
+ // This check might be redundant if called correctly from tick(), but good for safety
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Processing async read request off-thread");
+
+ try {
+ Object result = executeReadRequest(request);
+ request.future().complete(result);
+ } catch (Throwable t) {
+ // Log the error from the tick thread side
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Exception processing buffered async world read for type " + request.type(), t);
+ request.future().completeExceptionally(t);
+ }
+ }
+ }
+
+ // Executes the actual read operation based on the request type
+ private Object executeReadRequest(org.dreeam.leaf.async.world.WorldReadRequest request) {
+ Object[] params = request.params();
+ BlockPos pos; // Declare pos outside the switch
+
+ switch (request.type()) {
+ case BLOCK_GET_NMS_STATE: { //
+ pos = (BlockPos) params[0];
+ return this.getBlockState(pos);
+ }
+ case BLOCK_GET_BIOME: {
+ pos = (BlockPos) params[0];
+ return this.getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2);
+ }
+ case BLOCK_GET_COMPUTED_BIOME: {
+ pos = (BlockPos) params[0];
+ return this.getBiome(pos);
+ }
+ case BLOCK_IS_INDIRECTLY_POWERED: {
+ pos = (BlockPos) params[0];
+ return this.hasNeighborSignal(pos);
+ }
+ case BLOCK_GET_BLOCK_POWER: {
+ pos = (BlockPos) params[0];
+ org.bukkit.block.BlockFace face = (org.bukkit.block.BlockFace) params[1];
+ int power = 0;
+ Direction notchDir = org.bukkit.craftbukkit.block.CraftBlock.blockFaceToNotch(face);
+
+ if ((face == org.bukkit.block.BlockFace.DOWN || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.below(), Direction.DOWN)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.below()));
+ if ((face == org.bukkit.block.BlockFace.UP || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.above(), Direction.UP)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.above()));
+ if ((face == org.bukkit.block.BlockFace.EAST || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.east(), Direction.EAST)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.east()));
+ if ((face == org.bukkit.block.BlockFace.WEST || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.west(), Direction.WEST)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.west()));
+ if ((face == org.bukkit.block.BlockFace.NORTH || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.north(), Direction.NORTH)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.north()));
+ if ((face == org.bukkit.block.BlockFace.SOUTH || face == org.bukkit.block.BlockFace.SELF) && this.hasSignal(pos.south(), Direction.SOUTH)) power = org.bukkit.craftbukkit.block.CraftBlock.getPower(power, this.getBlockState(pos.south()));
+
+ boolean indirectlyPowered = (face == org.bukkit.block.BlockFace.SELF) ? this.hasNeighborSignal(pos) : (this.getSignal(pos, notchDir) > 0); // Simplified indirect check for faces
+ return power > 0 ? power : (indirectlyPowered ? 15 : 0);
+ }
+ case BLOCK_RAY_TRACE: {
+ pos = (BlockPos) params[0];
+ org.bukkit.Location start = (org.bukkit.Location) params[1];
+ org.bukkit.util.Vector direction = (org.bukkit.util.Vector) params[2];
+ double maxDistance = (double) params[3];
+ org.bukkit.FluidCollisionMode fluidCollisionMode = (org.bukkit.FluidCollisionMode) params[4];
+
+ org.bukkit.util.Vector dir = direction.clone().normalize().multiply(maxDistance);
+ Vec3 startPos = org.bukkit.craftbukkit.util.CraftLocation.toVec3D(start);
+ Vec3 endPos = startPos.add(dir.getX(), dir.getY(), dir.getZ());
+
+ return this.clip(new net.minecraft.world.level.ClipContext(startPos, endPos, net.minecraft.world.level.ClipContext.Block.OUTLINE, org.bukkit.craftbukkit.CraftFluidCollisionMode.toNMS(fluidCollisionMode), net.minecraft.world.phys.shapes.CollisionContext.empty()), pos); // Pass block pos
+ }
+ case BLOCK_CAN_PLACE: {
+ pos = (BlockPos) params[0];
+ org.bukkit.block.data.BlockData data = (org.bukkit.block.data.BlockData) params[1];
+ net.minecraft.world.level.block.state.BlockState nmsData = ((org.bukkit.craftbukkit.block.data.CraftBlockData) data).getState();
+ return nmsData.canSurvive(this, pos);
+ }
+ // Add cases for other ReadOperationType values here...
+ // case GET_ENTITIES_IN_BOX: ... (complex, needs careful list handling)
+
+ default:
+ throw new UnsupportedOperationException("Unsupported buffered read type: " + request.type());
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking
+
public void tick(BooleanSupplier hasTimeLeft) {
this.handlingTick = true;
+ this.processAsyncReadRequests(); // Leaf - SparklyPaper - parallel world ticking
TickRateManager tickRateManager = this.tickRateManager();
boolean runsNormally = tickRateManager.runsNormally();
if (runsNormally) {
@@ -746,6 +870,14 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.advanceWeatherCycle();
}
+ // Leaf start - SparklyPaper - parallel world ticking
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ this.moonrise$midTickTasks();
+ } else if ((++this.tickedBlocksOrFluids & 7L) != 0L) { // Keep original mid-tick logic for PWT enabled
+ this.server.moonrise$executeMidTickTasks();
+ }
+ // Leaf end - SparklyPaper - parallel world ticking
+
int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE);
if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) { // Purpur - Config for skipping night
// Paper start - create time skip event - move up calculations
@@ -1313,9 +1445,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
fluidState.tick(this, pos, blockState);
}
// Paper start - rewrite chunk system
@@ -611,7 +774,7 @@ index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d
// Paper end - rewrite chunk system
}
@@ -1326,9 +1331,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1326,9 +1461,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
blockState.tick(this, pos, this.random);
}
// Paper start - rewrite chunk system
@@ -626,7 +789,7 @@ index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d
// Paper end - rewrite chunk system
}
@@ -1579,6 +1587,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1579,6 +1717,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
private void addPlayer(ServerPlayer player) {
@@ -635,7 +798,7 @@ index a66e5f6652d9633c856490de36d8d8fdf8a5298a..60e0296312030d25f917c568c17ce86d
Entity entity = this.getEntities().get(player.getUUID());
if (entity != null) {
LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
@@ -1591,7 +1601,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1591,7 +1731,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// CraftBukkit start
private boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {

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 ac751d460ae0c8dbb858c4047c459a11b57ae175..24926aa7ed5c78b235659daf18b224b14beb744c 100644
index 7f791ff5bdf6c29c78f863d21af16270a74d8e7e..1431a0fac3c8a846535c1bd2f60a1279d08f14ea 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1693,7 +1693,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1699,7 +1699,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 {
@@ -27,10 +27,10 @@ index ac751d460ae0c8dbb858c4047c459a11b57ae175..24926aa7ed5c78b235659daf18b224b1
CrashReport crashReport = CrashReport.forThrowable(levelTickingException, "Exception ticking world");
serverLevel.fillReportDetails(crashReport);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 31abf2da10bc9b4b7825ed4b3d4e9da52feb2e39..9ba1c29b75ba0eb545097eef4fe568c53ebd885c 100644
index d6524d5c442555eaeb4d90f6a101262ee669f0d9..5c1f99a8fd9920b30e6a80769bc306591510012a 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -573,6 +573,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -574,6 +574,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper end - chunk tick iteration

View File

@@ -12,10 +12,10 @@ Original project: https://github.com/LeavesMC/Leaves
Commit: 41476d86922416c45f703df2871890831fc42bb5
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index aa6f0588f282e3152ef9639d6fb2b8cd1d54bdb7..514c2d414ac59c71929a7686204465c72d122513 100644
index ddf9a5d3af7ebe8e5c69d7dcb8a1c3a28d8178e3..3fabeaa91832eacc103416682aec3ce6121858fb 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -508,6 +508,7 @@ public final class CraftServer implements Server {
@@ -509,6 +509,7 @@ public final class CraftServer implements Server {
this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
this.spark = new io.papermc.paper.SparksFly(this); // Paper - spark
@@ -23,7 +23,7 @@ index aa6f0588f282e3152ef9639d6fb2b8cd1d54bdb7..514c2d414ac59c71929a7686204465c7
}
public boolean getCommandBlockOverride(String command) {
@@ -1140,6 +1141,7 @@ public final class CraftServer implements Server {
@@ -1141,6 +1142,7 @@ public final class CraftServer implements Server {
org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - Purpur config files
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");

View File

@@ -27,10 +27,10 @@ index 4c003acccdd2dd17918b15316001e52e7670123e..780f3a48152fef6a06dc67bf7fbd1965
HandlerList handlers = event.getHandlers();
RegisteredListener[] listeners = handlers.getRegisteredListeners();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index eec03c52172b8250c18318094093c752da97dbe6..f57e12a72cabe279a6a465f4397b44621a2d5cb0 100644
index 3fabeaa91832eacc103416682aec3ce6121858fb..7ca77be262e6c8c9882db42295a42487b672d43e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -322,6 +322,8 @@ public final class CraftServer implements Server {
@@ -323,6 +323,8 @@ public final class CraftServer implements Server {
private final io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler asyncScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler();
private final io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler globalRegionScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler();
@@ -39,7 +39,7 @@ index eec03c52172b8250c18318094093c752da97dbe6..f57e12a72cabe279a6a465f4397b4462
@Override
public final io.papermc.paper.threadedregions.scheduler.RegionScheduler getRegionScheduler() {
return this.regionizedScheduler;
@@ -410,7 +412,7 @@ public final class CraftServer implements Server {
@@ -411,7 +413,7 @@ public final class CraftServer implements Server {
public CraftServer(DedicatedServer console, PlayerList playerList) {
this.console = console;
this.playerList = (DedicatedPlayerList) playerList;
@@ -48,7 +48,7 @@ index eec03c52172b8250c18318094093c752da97dbe6..f57e12a72cabe279a6a465f4397b4462
@Override
public CraftPlayer apply(ServerPlayer player) {
return player.getBukkitEntity();
@@ -3456,4 +3458,11 @@ public final class CraftServer implements Server {
@@ -3461,4 +3463,11 @@ public final class CraftServer implements Server {
return getServer().lagging;
}
// Purpur end - Lagging threshold
@@ -74,10 +74,10 @@ index 8635cd772c5c2ae0ba326812ff2a1a179285a86f..cc024874fbde9678bdddfdca7c250808
if (entity instanceof EnderDragonPart complexPart) {
if (complexPart.parentMob instanceof EnderDragon) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 2f027c504859f7ef41ef243bbc16535c6595ec28..abc762829bc0447936ab9e06eabcb42419578585 100644
index 9d733dd366d54b0780746a6235d81dc17607cad2..7bd58d683ca2534d36510590e0895dce7c46551a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2281,7 +2281,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -2280,7 +2280,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public boolean canSee(Player player) {

View File

@@ -1,703 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Altiami <yoshimo.kristin@gmail.com>
Date: Wed, 5 Mar 2025 13:16:44 -0800
Subject: [PATCH] SparklyPaper: Parallel world ticking
Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
index a4aa2615823d77920ff55b8aa0bcc27a54b8c3e1..2fb65ce228da94eb7d9364ee0f94582300178f1d 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
@@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
+ private static final boolean HARD_THROW = !org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.disableHardThrow; // SparklyPaper - parallel world ticking - THIS SHOULD NOT BE DISABLED SINCE IT CAN CAUSE DATA CORRUPTION!!! Anyhow, for production servers, if you want to make a test run to see if the server could crash, you can test it with this disabled
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
@@ -26,14 +27,14 @@ public class TickThread extends Thread {
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
- throw new IllegalStateException(reason);
+ if (HARD_THROW) throw new IllegalStateException(reason); // SparklyPaper - parallel world ticking
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -42,7 +43,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -51,7 +52,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -60,7 +61,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ) + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -69,7 +70,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
+ reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity) + " - " + getTickThreadInformation(entity.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -78,7 +79,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -87,12 +88,70 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ) + " - " + getTickThreadInformation(world.getServer()); // SparklyPaper - parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
+ // SparklyPaper start - parallel world ticking
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
+ public static void ensureTickThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ if (!isTickThreadFor(world)) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable()); // SparklyPaper - parallel world ticking
+ if (HARD_THROW) throw new IllegalStateException(reason);
+ }
+ }
+
+ // This is an additional method to check if it is a tick thread but ONLY a tick thread
+ public static void ensureOnlyTickThread(final String reason) {
+ boolean isTickThread = isTickThread();
+ boolean isServerLevelTickThread = isServerLevelTickThread();
+ if (!isTickThread || isServerLevelTickThread) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread ONLY tick thread check: " + reason, new Throwable());
+ if (HARD_THROW) throw new IllegalStateException(reason);
+ }
+ }
+
+ // This is an additional method to check if the tick thread is bound to a specific world or if it is an async thread.
+ public static void ensureTickThreadOrAsyncThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ boolean isValidTickThread = isTickThreadFor(world);
+ boolean isAsyncThread = !isTickThread();
+ boolean isValid = isAsyncThread || isValidTickThread;
+ if (!isValid) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread or async thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable());
+ if (HARD_THROW) throw new IllegalStateException(reason);
+ }
+ }
+
+ public static String getTickThreadInformation(net.minecraft.server.MinecraftServer minecraftServer) {
+ StringBuilder sb = new StringBuilder();
+ Thread currentThread = Thread.currentThread();
+ sb.append("Is tick thread? ");
+ sb.append(currentThread instanceof TickThread);
+ sb.append("; Is server level tick thread? ");
+ sb.append(currentThread instanceof ServerLevelTickThread);
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ sb.append("; Currently ticking level: ");
+ if (serverLevelTickThread.currentlyTickingServerLevel != null) {
+ sb.append(serverLevelTickThread.currentlyTickingServerLevel.getWorld().getName());
+ } else {
+ sb.append("null");
+ }
+ }
+ sb.append("; Is iterating over levels? ");
+ sb.append(minecraftServer.isIteratingOverLevels);
+ sb.append("; Are we going to hard throw? ");
+ sb.append(HARD_THROW);
+ return sb.toString();
+ }
+
+ public static boolean isServerLevelTickThread() {
+ return Thread.currentThread() instanceof ServerLevelTickThread;
+ }
+ // SparklyPaper end - parallel world ticking
+
public final int id; /* We don't override getId as the spec requires that it be unique (with respect to all other threads) */
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
@@ -133,46 +192,74 @@ public class TickThread extends Thread {
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (add missing replacement / use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final Vec3 pos) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final AABB aabb) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final double blockX, final double blockZ) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final Vec3 position, final Vec3 deltaMovement, final int buffer) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final int fromChunkX, final int fromChunkZ, final int toChunkX, final int toChunkZ) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ, final int radius) {
- return isTickThread();
+ return isTickThreadFor(world); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
+ }
+
+ // SparklyPaper start - parallel world ticking
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
+ public static boolean isTickThreadFor(final Level world) {
+ if (Thread.currentThread() instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else {
+ return isTickThread();
+ }
}
public static boolean isTickThreadFor(final Entity entity) {
- return isTickThread();
+ if (entity == null) {
+ return true;
+ }
+
+ return isTickThreadFor(entity.level()); // Leaf - SparklyPaper - parallel world ticking mod (use methods for what they were made for)
+ }
+
+ public static class ServerLevelTickThread extends TickThread {
+ public ServerLevelTickThread(String name) {
+ super(name);
+ }
+
+ public ServerLevelTickThread(Runnable run, String name) {
+ super(run, name);
+ }
+
+ public net.minecraft.server.level.ServerLevel currentlyTickingServerLevel;
}
+ // SparklyPaper end - parallel world ticking
}
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index 548fcd9646dee0c40b6ba9b3dafb9ca157dfe324..d7af94890bfccd6ff665d920cecfa1e5be626aa4 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -40,6 +40,12 @@ class PaperEventManager {
if (listeners.length == 0) return;
// Leaf end - Skip event if no listeners
if (event.isAsynchronous() && this.server.isPrimaryThread()) {
+ // Leaf start - Parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) {
+ org.dreeam.leaf.async.world.PWTEventScheduler.getScheduler().scheduleTask(event::callEvent);
+ return;
+ }
+ // Leaf end - Parallel world ticking
throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
} else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) {
// Leaf start - Multithreaded tracker
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index af33cab59932f4ec135caf94dc5828930833daf6..92463ddc6fdcf542ce4a6d2a5059d4a9f7a6085a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -455,7 +455,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
private boolean unloadChunk0(int x, int z, boolean save) {
- org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot unload chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
+ else
+ org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
if (!this.isChunkLoaded(x, z)) {
return true;
}
@@ -472,6 +477,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean refreshChunk(int x, int z) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot refresh chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
if (playerChunk == null) return false;
@@ -522,7 +529,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
- org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.getHandle(), x, z, "May not sync load chunks asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
+ else
+ org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
@@ -750,6 +762,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, loc.getX(), loc.getZ(), "Cannot generate tree asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.world.captureTreeGeneration = true;
this.world.captureBlockStates = true;
boolean grownTree = this.generateTree(loc, type);
@@ -865,6 +879,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer<net.minecraft.world.level.ServerExplosion> configurator) {
// Paper end - expand explosion API
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot create explosion asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
net.minecraft.world.level.Level.ExplosionInteraction explosionType;
if (!breakBlocks) {
explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks
@@ -956,6 +972,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x >> 4, z >> 4, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
warnUnsafeChunk("getting a faraway chunk", x >> 4, z >> 4); // Paper
// Transient load for this tick
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
@@ -986,6 +1004,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> bb) {
BlockPos pos = new BlockPos(x, 0, z);
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, pos, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
if (this.world.hasChunkAt(pos)) {
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos);
@@ -2328,6 +2348,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, position.getX(), position.getZ(), "Cannot send game event asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
getHandle().gameEvent(sourceEntity != null ? ((CraftEntity) sourceEntity).getHandle(): null, net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(gameEvent.getKey())).orElseThrow(), org.bukkit.craftbukkit.util.CraftVector.toBlockPos(position));
}
// Paper end
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 811823a1a7e24a19a7e37eb4c08efdfa19e839ed..b94efcab12d41fa8745e5bb55cfa6481d8262e74 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -75,6 +75,11 @@ public class CraftBlock implements Block {
}
public net.minecraft.world.level.block.state.BlockState getNMS() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.world.getBlockState(this.position);
}
@@ -157,6 +162,11 @@ public class CraftBlock implements Block {
}
private void setData(final byte data, int flag) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flag);
}
@@ -198,6 +208,12 @@ public class CraftBlock implements Block {
}
public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
+
// SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup
if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes
// SPIGOT-4612: faster - just clear tile
@@ -343,18 +359,33 @@ public class CraftBlock implements Block {
@Override
public Biome getBiome() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ());
}
// Paper start
@Override
public Biome getComputedBiome() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ());
}
// Paper end
@Override
public void setBiome(Biome bio) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
}
@@ -375,6 +406,11 @@ public class CraftBlock implements Block {
@Override
public boolean isBlockIndirectlyPowered() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.world.getMinecraftWorld().hasNeighborSignal(this.position);
}
@@ -414,6 +450,11 @@ public class CraftBlock implements Block {
@Override
public int getBlockPower(BlockFace face) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
int power = 0;
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
int x = this.getX();
@@ -484,6 +525,11 @@ public class CraftBlock implements Block {
@Override
public boolean breakNaturally() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
return this.breakNaturally(null);
}
@@ -543,6 +589,11 @@ public class CraftBlock implements Block {
@Override
public boolean applyBoneMeal(BlockFace face) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Direction direction = CraftBlock.blockFaceToNotch(face);
BlockFertilizeEvent event = null;
ServerLevel world = this.getCraftWorld().getHandle();
@@ -554,8 +605,8 @@ public class CraftBlock implements Block {
world.captureTreeGeneration = false;
if (world.capturedBlockStates.size() > 0) {
- TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null;
+ TreeType treeType = SaplingBlock.getTreeTypeRT(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ SaplingBlock.setTreeTypeRT(null); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
List<BlockState> blocks = new ArrayList<>(world.capturedBlockStates.values());
world.capturedBlockStates.clear();
StructureGrowEvent structureEvent = null;
@@ -644,6 +695,11 @@ public class CraftBlock implements Block {
@Override
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Preconditions.checkArgument(start != null, "Location start cannot be null");
Preconditions.checkArgument(this.getWorld().equals(start.getWorld()), "Location start cannot be a different world");
start.checkFinite();
@@ -685,6 +741,11 @@ public class CraftBlock implements Block {
@Override
public boolean canPlace(BlockData data) {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
Preconditions.checkArgument(data != null, "BlockData cannot be null");
net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState();
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
@@ -719,6 +780,11 @@ public class CraftBlock implements Block {
@Override
public void tick() {
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && world instanceof ServerLevel serverWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
final ServerLevel level = this.world.getMinecraftWorld();
this.getNMS().tick(level, this.position, level.random);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index 768d3f93da2522d467183654260a8bd8653588b1..5cef786fa2e5dfd3e7b79918bc73af02891b0bea 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -26,6 +26,27 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
private final T snapshot;
public boolean snapshotDisabled; // Paper
public static boolean DISABLE_SNAPSHOT = false; // Paper
+ public static ThreadLocal<Boolean> DISABLE_SNAPSHOT_TL = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (distinguish name)
+
+ // Leaf start - SparklyPaper - parallel world ticking mod
+ // refer to original field in case plugins attempt to modify it
+ public static boolean getDisableSnapshotTL() {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && DISABLE_SNAPSHOT_TL.get())
+ return true;
+ synchronized (CraftBlockEntityState.class) {
+ return DISABLE_SNAPSHOT;
+ }
+ }
+
+ // update original field in case plugins attempt to access it
+ public static void setDisableSnapshotTL(boolean value) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ DISABLE_SNAPSHOT_TL.set(value);
+ synchronized (CraftBlockEntityState.class) {
+ DISABLE_SNAPSHOT = value;
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod
public CraftBlockEntityState(World world, T tileEntity) {
super(world, tileEntity.getBlockPos(), tileEntity.getBlockState());
@@ -34,8 +55,8 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
try { // Paper - Show blockstate location if we failed to read it
// Paper start
- this.snapshotDisabled = DISABLE_SNAPSHOT;
- if (DISABLE_SNAPSHOT) {
+ this.snapshotDisabled = getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ if (this.snapshotDisabled) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
this.snapshot = this.tileEntity;
} else {
this.snapshot = this.createSnapshot(tileEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
index fa63a6cfcfcc4eee4503a82d85333c139c8c8b2b..d106f65e4b745242484a195958fc559268a7dee0 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
@@ -215,6 +215,12 @@ public class CraftBlockState implements BlockState {
LevelAccessor access = this.getWorldHandle();
CraftBlock block = this.getBlock();
+ // SparklyPaper start - parallel world ticking
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && access instanceof net.minecraft.server.level.ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // SparklyPaper end - parallel world ticking
+
if (block.getType() != this.getType()) {
if (!force) {
return false;
@@ -350,6 +356,8 @@ public class CraftBlockState implements BlockState {
@Override
public java.util.Collection<org.bukkit.inventory.ItemStack> getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(world.getHandle(), position, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking
this.requirePlaced();
net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
index 55572e799b5c8a74a546ac8febc14f80d5731c52..08a06c23c831a4de45b3e537228b837911019da8 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
@@ -249,8 +249,8 @@ public final class CraftBlockStates {
net.minecraft.world.level.block.state.BlockState blockData = craftBlock.getNMS();
BlockEntity tileEntity = craftBlock.getHandle().getBlockEntity(blockPosition);
// Paper start - block state snapshots
- boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
- CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
+ boolean prev = CraftBlockEntityState.getDisableSnapshotTL(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ CraftBlockEntityState.setDisableSnapshotTL(!useSnapshot); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
try {
// Paper end
CraftBlockState blockState = CraftBlockStates.getBlockState(world, blockPosition, blockData, tileEntity);
@@ -258,7 +258,7 @@ public final class CraftBlockStates {
return blockState;
// Paper start
} finally {
- CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
+ CraftBlockEntityState.setDisableSnapshotTL(prev); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
}
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
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 {
}
public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
+ public static final ThreadLocal<BlockPos> sourceBlockOverrideRT = new ThreadLocal<>(); // SparklyPaper - parallel world ticking (this is from Folia, fixes concurrency bugs with sculk catalysts)
+
+ // Leaf start - SparklyPaper - parallel world ticking mod
+ // refer to original field in case plugins attempt to modify it
+ public static BlockPos getSourceBlockOverrideRT() {
+ BlockPos sourceBlockOverrideRTCopy;
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (sourceBlockOverrideRTCopy = sourceBlockOverrideRT.get()) != null)
+ return sourceBlockOverrideRTCopy;
+ synchronized (CraftEventFactory.class) {
+ return sourceBlockOverride;
+ }
+ }
+
+ // update original field in case plugins attempt to access it
+ public static void setSourceBlockOverrideRT(BlockPos value) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ sourceBlockOverrideRT.set(value);
+ synchronized (CraftEventFactory.class) {
+ sourceBlockOverride = value;
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod
public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState block, int flag) {
// Suppress during worldgen
@@ -972,7 +994,10 @@ public class CraftEventFactory {
CraftBlockState state = CraftBlockStates.getBlockState(world, target, flag);
state.setData(block);
- BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), state);
+ // Leaf start - SparklyPaper parallel world ticking mod (collapse original behavior)
+ final BlockPos sourceBlockOverrideRTSnap = getSourceBlockOverrideRT();
+ BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, sourceBlockOverrideRTSnap != null ? sourceBlockOverrideRTSnap : source), state); // SparklyPaper - parallel world ticking
+ // Leaf end - SparklyPaper parallel world ticking mod (collapse original behavior)
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
@@ -2265,7 +2290,7 @@ public class CraftEventFactory {
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemStack.copyWithCount(1));
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), CraftVector.toBukkit(to));
- if (!net.minecraft.world.level.block.DispenserBlock.eventFired) {
+ if (!net.minecraft.world.level.block.DispenserBlock.getEventFiredTL()) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
if (!event.callEvent()) {
return itemStack;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..e7c6b2ab5f2c68f3319ccd52785c8d3488a2eef7 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
@@ -19,11 +19,39 @@ class CraftAsyncTask extends CraftTask {
@Override
public boolean isSync() {
+ // Leaf start - Parallel world ticking
+ // Return true if we should run this task synchronously when parallel world ticking is enabled and runAsyncTasksSync is true
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled &&
+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) {
+ return true;
+ }
+ // Leaf end - Parallel world ticking
return false;
}
@Override
public void run() {
+ // Leaf start - Parallel world ticking
+ // If parallel world ticking is enabled and we're configured to run async tasks sync,
+ // execute the task as if it were a sync task (directly on the main thread)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled &&
+ org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.runAsyncTasksSync) {
+ try {
+ super.run();
+ } catch (final Throwable t) {
+ this.getOwner().getLogger().log(
+ Level.WARNING,
+ String.format(
+ "Plugin %s generated an exception while executing task %s (forced sync mode)",
+ this.getOwner().getDescription().getFullName(),
+ this.getTaskId()),
+ t);
+ }
+ return;
+ }
+ // Leaf end - Parallel world ticking
+
+ // Original async implementation
final Thread thread = Thread.currentThread();
// Paper start - name threads according to running plugin
final String nameBefore = thread.getName();

View File

@@ -0,0 +1,11 @@
package org.dreeam.leaf.async.world;
public enum ReadOperationType {
BLOCK_GET_BIOME,
BLOCK_GET_COMPUTED_BIOME,
BLOCK_IS_INDIRECTLY_POWERED,
BLOCK_GET_BLOCK_POWER,
BLOCK_RAY_TRACE,
BLOCK_CAN_PLACE,
BLOCK_GET_NMS_STATE
}

View File

@@ -0,0 +1,10 @@
package org.dreeam.leaf.async.world;
import java.util.concurrent.CompletableFuture;
public record WorldReadRequest(
ReadOperationType type,
Object[] params, // Parameters for the read operation
CompletableFuture<Object> future // Future to complete with the result
) {
}

View File

@@ -2,6 +2,8 @@ package org.dreeam.leaf.config.modules.network;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
import org.dreeam.leaf.protocol.DoABarrelRollPackets;
import org.dreeam.leaf.protocol.DoABarrelRollProtocol;
import java.util.concurrent.ThreadLocalRandom;

View File

@@ -33,6 +33,8 @@ import net.minecraft.world.level.ChunkPos;
import org.dreeam.leaf.config.modules.misc.RegionFormatConfig;
import org.slf4j.Logger;
import net.minecraft.server.MinecraftServer;
public class LinearRegionFile implements IRegionFile {
private static final long SUPERBLOCK = -4323716122432332390L;
@@ -148,6 +150,7 @@ public class LinearRegionFile implements IRegionFile {
// Save only once on shutdown
if (!closed) return;
}
long timestamp = getTimestamp();
short chunkCount = 0;

View File

@@ -1,405 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Date: Sat, 17 Feb 2024 17:57:08 -0500
Subject: [PATCH] Rail Optimization: optimized PoweredRailBlock logic
Original project: https://github.com/FxMorin/RailOptimization
Full Rewrite of the powered rail iteration logic
that makes powered/activator rails turning on/off up to 4x faster.
This rewrite brings a massive performance boost while keeping the vanilla order. This is achieved by running all the
powered rail logic from a single rail instead of each block iterating separately. Which was not only very
expensive but also completely unnecessary and with a lot of massive overhead
diff --git a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
index bd14c08defe8afc5ceca59d16a5b1dbad178f594..b37ab12d4c51aca1576a14147a959188031d0fd7 100644
--- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java
@@ -29,7 +29,7 @@ public class PoweredRailBlock extends BaseRailBlock {
this.registerDefaultState((BlockState) ((BlockState) ((BlockState) ((BlockState) this.stateDefinition.any()).setValue(PoweredRailBlock.SHAPE, RailShape.NORTH_SOUTH)).setValue(PoweredRailBlock.POWERED, false)).setValue(PoweredRailBlock.WATERLOGGED, false));
}
- protected boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) {
+ public boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) { // Leaf - Rail Optimization - protected -> public
if (distance >= world.purpurConfig.railActivationRange) { // Purpur
return false;
} else {
@@ -117,6 +117,12 @@ public class PoweredRailBlock extends BaseRailBlock {
@Override
protected void updateState(BlockState state, Level world, BlockPos pos, Block neighbor) {
+ // Leaf start - Rail Optimization
+ if (org.dreeam.leaf.config.modules.opt.OptimizedPoweredRails.enabled) {
+ org.dreeam.leaf.optimize.OptimizedPoweredRails.customUpdateState(this, state, world, pos);
+ return;
+ }
+ // Leaf end - Rail Optimization
boolean flag = (Boolean) state.getValue(PoweredRailBlock.POWERED);
boolean flag1 = world.hasNeighborSignal(pos) || this.findPoweredRailSignal(world, pos, state, true, 0) || this.findPoweredRailSignal(world, pos, state, false, 0);
diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java
new file mode 100644
index 0000000000000000000000000000000000000000..ded3c385fcfc6c35086f76bed1b2223a6a29a43e
--- /dev/null
+++ b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java
@@ -0,0 +1,18 @@
+package org.dreeam.leaf.config.modules.opt;
+
+import org.dreeam.leaf.config.ConfigModules;
+import org.dreeam.leaf.config.EnumConfigCategory;
+
+public class OptimizedPoweredRails extends ConfigModules {
+
+ public String getBasePath() {
+ return EnumConfigCategory.PERF.getBaseKeyName() + ".optimized-powered-rails";
+ }
+
+ public static boolean enabled = true;
+
+ @Override
+ public void onLoaded() {
+ enabled = config().getBoolean(getBasePath(), enabled);
+ }
+}
diff --git a/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java b/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e8bc27585e204aa2df77a90418bbe9e00bcd040
--- /dev/null
+++ b/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java
@@ -0,0 +1,335 @@
+package org.dreeam.leaf.optimize;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.PoweredRailBlock;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.RailShape;
+
+import java.util.HashMap;
+
+import static net.minecraft.world.level.block.Block.*;
+import static net.minecraft.world.level.block.PoweredRailBlock.POWERED;
+import static net.minecraft.world.level.block.PoweredRailBlock.SHAPE;
+
+public class OptimizedPoweredRails {
+
+ private static final Direction[] EAST_WEST_DIR = new Direction[]{Direction.WEST, Direction.EAST};
+ private static final Direction[] NORTH_SOUTH_DIR = new Direction[]{Direction.SOUTH, Direction.NORTH};
+
+ private static final int UPDATE_FORCE_PLACE = UPDATE_MOVE_BY_PISTON | UPDATE_KNOWN_SHAPE | UPDATE_CLIENTS;
+
+ public static int RAIL_POWER_LIMIT = 8;
+
+ public static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) {
+ BlockState oldState = level.getBlockState(pos);
+ Block.updateOrDestroy(
+ oldState,
+ oldState.updateShape(direction.getOpposite(), state, level, pos, fromPos),
+ level,
+ pos,
+ UPDATE_CLIENTS & -34,
+ 0
+ );
+ }
+
+ public static void setRailPowerLimit(int powerLimit) {
+ RAIL_POWER_LIMIT = powerLimit;
+ }
+
+ public static void customUpdateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) {
+ boolean shouldBePowered = level.hasNeighborSignal(pos) ||
+ self.findPoweredRailSignal(level, pos, state, true, 0) ||
+ self.findPoweredRailSignal(level, pos, state, false, 0);
+ if (shouldBePowered != state.getValue(POWERED)) {
+ RailShape railShape = state.getValue(SHAPE);
+ if (railShape.isAscending()) {
+ level.setBlock(pos, state.setValue(POWERED, shouldBePowered), 3);
+ level.updateNeighborsAtExceptFromFacing(pos.below(), self, Direction.UP);
+ level.updateNeighborsAtExceptFromFacing(pos.above(), self, Direction.DOWN); //isAscending
+ } else if (shouldBePowered) {
+ powerLane(self, level, pos, state, railShape);
+ } else {
+ dePowerLane(self, level, pos, state, railShape);
+ }
+ }
+ }
+
+ public static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level world, BlockPos pos,
+ boolean bl, int distance, RailShape shape,
+ HashMap<BlockPos, Boolean> checkedPos) {
+ BlockState blockState = world.getBlockState(pos);
+ boolean speedCheck = checkedPos.containsKey(pos) && checkedPos.get(pos);
+ if (speedCheck) {
+ return world.hasNeighborSignal(pos) ||
+ findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos);
+ } else {
+ if (blockState.is(self)) {
+ RailShape railShape = blockState.getValue(SHAPE);
+ if (shape == RailShape.EAST_WEST && (
+ railShape == RailShape.NORTH_SOUTH ||
+ railShape == RailShape.ASCENDING_NORTH ||
+ railShape == RailShape.ASCENDING_SOUTH
+ ) || shape == RailShape.NORTH_SOUTH && (
+ railShape == RailShape.EAST_WEST ||
+ railShape == RailShape.ASCENDING_EAST ||
+ railShape == RailShape.ASCENDING_WEST
+ )) {
+ return false;
+ } else if (blockState.getValue(POWERED)) {
+ return world.hasNeighborSignal(pos) ||
+ findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos);
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+ }
+
+ public static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level,
+ BlockPos pos, BlockState state, boolean bl, int distance,
+ HashMap<BlockPos, Boolean> checkedPos) {
+ if (distance >= RAIL_POWER_LIMIT - 1) return false;
+ int i = pos.getX();
+ int j = pos.getY();
+ int k = pos.getZ();
+ boolean bl2 = true;
+ RailShape railShape = state.getValue(SHAPE);
+ switch (railShape.ordinal()) {
+ case 0 -> {
+ if (bl) ++k;
+ else --k;
+ }
+ case 1 -> {
+ if (bl) --i;
+ else ++i;
+ }
+ case 2 -> {
+ if (bl) {
+ --i;
+ } else {
+ ++i;
+ ++j;
+ bl2 = false;
+ }
+ railShape = RailShape.EAST_WEST;
+ }
+ case 3 -> {
+ if (bl) {
+ --i;
+ ++j;
+ bl2 = false;
+ } else {
+ ++i;
+ }
+ railShape = RailShape.EAST_WEST;
+ }
+ case 4 -> {
+ if (bl) {
+ ++k;
+ } else {
+ --k;
+ ++j;
+ bl2 = false;
+ }
+ railShape = RailShape.NORTH_SOUTH;
+ }
+ case 5 -> {
+ if (bl) {
+ ++k;
+ ++j;
+ bl2 = false;
+ } else {
+ --k;
+ }
+ railShape = RailShape.NORTH_SOUTH;
+ }
+ }
+ return findPoweredRailSignalFaster(
+ self, level, new BlockPos(i, j, k),
+ bl, distance, railShape, checkedPos
+ ) ||
+ (bl2 && findPoweredRailSignalFaster(
+ self, level, new BlockPos(i, j - 1, k),
+ bl, distance, railShape, checkedPos
+ ));
+ }
+
+ public static void powerLane(PoweredRailBlock self, Level world, BlockPos pos,
+ BlockState mainState, RailShape railShape) {
+ world.setBlock(pos, mainState.setValue(POWERED, true), UPDATE_FORCE_PLACE);
+ HashMap<BlockPos, Boolean> checkedPos = new HashMap<>();
+ checkedPos.put(pos, true);
+ int[] count = new int[2];
+ if (railShape == RailShape.NORTH_SOUTH) { //Order: +z, -z
+ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
+ setRailPositionsPower(self, world, pos, checkedPos, count, i, NORTH_SOUTH_DIR[i]);
+ }
+ updateRails(self, false, world, pos, mainState, count);
+ } else if (railShape == RailShape.EAST_WEST) { //Order: -x, +x
+ for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
+ setRailPositionsPower(self, world, pos, checkedPos, count, i, EAST_WEST_DIR[i]);
+ }
+ updateRails(self, true, world, pos, mainState, count);
+ }
+ }
+
+ public static void dePowerLane(PoweredRailBlock self, Level world, BlockPos pos,
+ BlockState mainState, RailShape railShape) {
+ world.setBlock(pos, mainState.setValue(POWERED, false), UPDATE_FORCE_PLACE);
+ int[] count = new int[2];
+ if (railShape == RailShape.NORTH_SOUTH) { //Order: +z, -z
+ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
+ setRailPositionsDePower(self, world, pos, count, i, NORTH_SOUTH_DIR[i]);
+ }
+ updateRails(self, false, world, pos, mainState, count);
+ } else if (railShape == RailShape.EAST_WEST) { //Order: -x, +x
+ for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
+ setRailPositionsDePower(self, world, pos, count, i, EAST_WEST_DIR[i]);
+ }
+ updateRails(self, true, world, pos, mainState, count);
+ }
+ }
+
+ private static void setRailPositionsPower(PoweredRailBlock self, Level world, BlockPos pos,
+ HashMap<BlockPos, Boolean> checkedPos, int[] count, int i, Direction dir) {
+ for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
+ BlockPos newPos = pos.relative(dir, z);
+ BlockState state = world.getBlockState(newPos);
+ if (checkedPos.containsKey(newPos)) {
+ if (!checkedPos.get(newPos)) break;
+ count[i]++;
+ } else if (!state.is(self) || state.getValue(POWERED) || !(
+ world.hasNeighborSignal(newPos) ||
+ findPoweredRailSignalFaster(self, world, newPos, state, true, 0, checkedPos) ||
+ findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos)
+ )) {
+ checkedPos.put(newPos, false);
+ break;
+ } else {
+ checkedPos.put(newPos, true);
+ world.setBlock(newPos, state.setValue(POWERED, true), UPDATE_FORCE_PLACE);
+ count[i]++;
+ }
+ }
+ }
+
+ private static void setRailPositionsDePower(PoweredRailBlock self, Level world, BlockPos pos,
+ int[] count, int i, Direction dir) {
+ for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
+ BlockPos newPos = pos.relative(dir, z);
+ BlockState state = world.getBlockState(newPos);
+ if (!state.is(self) || !state.getValue(POWERED) || world.hasNeighborSignal(newPos) ||
+ self.findPoweredRailSignal(world, newPos, state, true, 0) ||
+ self.findPoweredRailSignal(world, newPos, state, false, 0)) break;
+ world.setBlock(newPos, state.setValue(POWERED, false), UPDATE_FORCE_PLACE);
+ count[i]++;
+ }
+ }
+
+ private static void shapeUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, BlockState mainState,
+ int endPos, Direction direction, int currentPos, BlockPos blockPos) {
+ if (currentPos == endPos) {
+ BlockPos newPos = pos.relative(direction, currentPos + 1);
+ OptimizedPoweredRails.giveShapeUpdate(world, mainState, newPos, pos, direction);
+ BlockState state = world.getBlockState(blockPos);
+ if (state.is(self) && state.getValue(SHAPE).isAscending())
+ OptimizedPoweredRails.giveShapeUpdate(world, mainState, newPos.above(), pos, direction);
+ }
+ }
+
+ private static void neighborUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, int endPos,
+ Direction direction, Block block, int currentPos, BlockPos blockPos) {
+ if (currentPos == endPos) {
+ BlockPos newPos = pos.relative(direction, currentPos + 1);
+ world.neighborChanged(newPos, block, pos);
+ BlockState state = world.getBlockState(blockPos);
+ if (state.is(self) && state.getValue(SHAPE).isAscending())
+ world.neighborChanged(newPos.above(), block, blockPos);
+ }
+ }
+
+ private static void updateRailsSectionEastWestShape(PoweredRailBlock self, Level world, BlockPos pos,
+ int c, BlockState mainState, Direction dir,
+ int[] count, int countAmt) {
+ BlockPos pos1 = pos.relative(dir, c);
+ if (c == 0 && count[1] == 0)
+ giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite());
+ shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1);
+ giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN);
+ giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP);
+ giveShapeUpdate(world, mainState, pos1.north(), pos, Direction.NORTH);
+ giveShapeUpdate(world, mainState, pos1.south(), pos, Direction.SOUTH);
+ }
+
+ private static void updateRailsSectionNorthSouthShape(PoweredRailBlock self, Level world, BlockPos pos,
+ int c, BlockState mainState, Direction dir,
+ int[] count, int countAmt) {
+ BlockPos pos1 = pos.relative(dir, c);
+ giveShapeUpdate(world, mainState, pos1.west(), pos, Direction.WEST);
+ giveShapeUpdate(world, mainState, pos1.east(), pos, Direction.EAST);
+ giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN);
+ giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP);
+ shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1);
+ if (c == 0 && count[1] == 0)
+ giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite());
+ }
+
+ private static void updateRails(PoweredRailBlock self, boolean eastWest, Level world,
+ BlockPos pos, BlockState mainState, int[] count) {
+ if (eastWest) {
+ for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
+ int countAmt = count[i];
+ if (i == 1 && countAmt == 0) continue;
+ Direction dir = EAST_WEST_DIR[i];
+ Block block = mainState.getBlock();
+ for (int c = countAmt; c >= i; c--) {
+ BlockPos p = pos.relative(dir, c);
+ if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, pos);
+ neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p);
+ world.neighborChanged(p.below(), block, pos);
+ world.neighborChanged(p.above(), block, pos);
+ world.neighborChanged(p.north(), block, pos);
+ world.neighborChanged(p.south(), block, pos);
+ BlockPos pos2 = pos.relative(dir, c).below();
+ world.neighborChanged(pos2.below(), block, pos);
+ world.neighborChanged(pos2.north(), block, pos);
+ world.neighborChanged(pos2.south(), block, pos);
+ if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, pos);
+ if (c == 0 && count[1] == 0)
+ world.neighborChanged(p.relative(dir.getOpposite()).below(), block, pos);
+ }
+ for (int c = countAmt; c >= i; c--)
+ updateRailsSectionEastWestShape(self, world, pos, c, mainState, dir, count, countAmt);
+ }
+ } else {
+ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
+ int countAmt = count[i];
+ if (i == 1 && countAmt == 0) continue;
+ Direction dir = NORTH_SOUTH_DIR[i];
+ Block block = mainState.getBlock();
+ for (int c = countAmt; c >= i; c--) {
+ BlockPos p = pos.relative(dir, c);
+ world.neighborChanged(p.west(), block, pos);
+ world.neighborChanged(p.east(), block, pos);
+ world.neighborChanged(p.below(), block, pos);
+ world.neighborChanged(p.above(), block, pos);
+ neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p);
+ if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, pos);
+ BlockPos pos2 = pos.relative(dir, c).below();
+ world.neighborChanged(pos2.west(), block, pos);
+ world.neighborChanged(pos2.east(), block, pos);
+ world.neighborChanged(pos2.below(), block, pos);
+ if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, pos);
+ if (c == 0 && count[1] == 0)
+ world.neighborChanged(p.relative(dir.getOpposite()).below(), block, pos);
+ }
+ for (int c = countAmt; c >= i; c--)
+ updateRailsSectionNorthSouthShape(self, world, pos, c, mainState, dir, count, countAmt);
+ }
+ }
+ }
+}
\ No newline at end of file

View File

@@ -15750,13 +15750,13 @@ index 248ac9bc820a96fc7653471308b18834fc735a77..ef70ba88e492904c426c7d35df442fa6
}
// CraftBukkit end
diff --git a/net/minecraft/world/level/block/PoweredRailBlock.java b/net/minecraft/world/level/block/PoweredRailBlock.java
index 5ec12356e9f044d99762bf64a6d3bf74856d97a6..e80069d27ff60e0967d9333e2a0d6c8bf2f20a16 100644
index 6c64fc260669266869f7495ff07e1270dcb4ac75..a2202d2b4352be07b2445064339c61ba6a2521c7 100644
--- a/net/minecraft/world/level/block/PoweredRailBlock.java
+++ b/net/minecraft/world/level/block/PoweredRailBlock.java
@@ -28,7 +28,7 @@ public class PoweredRailBlock extends BaseRailBlock {
}
protected boolean findPoweredRailSignal(Level level, BlockPos pos, BlockState state, boolean searchForward, int recursionCount) {
public boolean findPoweredRailSignal(Level level, BlockPos pos, BlockState state, boolean searchForward, int recursionCount) {
- if (recursionCount >= 8) {
+ if (recursionCount >= level.purpurConfig.railActivationRange) { // Purpur - Config for powered rail activation distance
return false;

View File

@@ -65,7 +65,7 @@ index 35fd539eb2bfe60ad17ab1e558a01273666acc54..445bbdc8da7f1fdbddfc4d8787d78fea
this.values[this.vp++ & 0xFF] = (int)(l * 100L / Runtime.getRuntime().maxMemory());
this.repaint();
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index d2c1da7df4961fddbb7db952d817f127ece275d9..39d4ae0ae1ec89007fe1c86dc8d3409902890552 100644
index 35ca166964e8436154891708f69ac010491b64aa..586c00610fdba178f27391820d623c3a5254529f 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1289,7 +1289,7 @@ public class ServerGamePacketListenerImpl
@@ -78,7 +78,7 @@ index d2c1da7df4961fddbb7db952d817f127ece275d9..39d4ae0ae1ec89007fe1c86dc8d34099
this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
return;
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 30e341bd5fca8b95d6911ca94f4aac17962f3269..55854dbacef9b3da9f12f67be21dd49007ea3c45 100644
index b714c64a318d38e309351f34426406c70d35d384..4e3b73bee5dae2b5921db86cc53b4cd9ebbd06f8 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -522,23 +522,36 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -128,7 +128,7 @@ index 30e341bd5fca8b95d6911ca94f4aac17962f3269..55854dbacef9b3da9f12f67be21dd490
public Entity(EntityType<?> entityType, Level level) {
this.type = entityType;
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index eb17ef1e0c350e97149452dbdc79398d71370fb1..d265278a56575529198735c113fa72c53b541669 100644
index cd8c764f9aa2321c6e157abe958d89868a9f7bd1..ed15b5f29658d799a36dcbd196a8fcb107be4bda 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1024,13 +1024,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
@@ -150,7 +150,7 @@ index eb17ef1e0c350e97149452dbdc79398d71370fb1..d265278a56575529198735c113fa72c5
}
// Purpur end - Mob head visibility percent
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index ba2f9ab55e52e25788b38c81e1070ae953b66371..33c99f3b24596fbbf3283b455ea8bf340203e01b 100644
index 6a94f42c8e048985b94db64fa0405e12824a8a0f..fbcf26c0bfb9f496c99bd5a2ba988be06162cd52 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -1505,10 +1505,6 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
@@ -279,7 +279,7 @@ index 584955d151e95727406bc68d6a6f15a33c0f920c..865a83fc73d79893e9bdedad37a6e398
HoglinAi.updateActivity(this);
if (this.isConverting()) {
diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java
index bac328ebdf9f94d156211e03d3913157e2b80374..bf1b33b7bb71ec8387d95d9089f199627a041945 100644
index 0afdfdc07764a26316c171c2a6722caf786875f2..732d44372cf226ca9d008ebc26d1838237ec96d6 100644
--- a/net/minecraft/world/entity/monster/piglin/Piglin.java
+++ b/net/minecraft/world/entity/monster/piglin/Piglin.java
@@ -357,8 +357,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
@@ -293,7 +293,7 @@ index bac328ebdf9f94d156211e03d3913157e2b80374..bf1b33b7bb71ec8387d95d9089f19962
PiglinAi.updateActivity(this);
super.customServerAiStep(level);
diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java
index bde9370798c037c494a9c73f328cb251e68c21b1..3a6be0122d49b20f78be6cc8f1c7acb5c2e45f39 100644
index 9f1c07be83999b7bafdee9238e8bad8c646d42f1..554e679a756dc1bf529053594231a958717f3573 100644
--- a/net/minecraft/world/entity/projectile/Projectile.java
+++ b/net/minecraft/world/entity/projectile/Projectile.java
@@ -85,7 +85,7 @@ public abstract class Projectile extends Entity implements TraceableEntity {
@@ -305,3 +305,21 @@ index bde9370798c037c494a9c73f328cb251e68c21b1..3a6be0122d49b20f78be6cc8f1c7acb5
} else if (maxProjectileChunkLoadsConfig.perProjectile.resetMovementAfterReachLimit) {
this.setDeltaMovement(0, this.getDeltaMovement().y, 0);
}
diff --git a/org/purpurmc/purpur/PurpurWorldConfig.java b/org/purpurmc/purpur/PurpurWorldConfig.java
index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..cadfbb7310fce33eda24d69c39fda5689c7fb882 100644
--- a/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -2435,6 +2435,13 @@ public class PurpurWorldConfig {
piglinMobGriefingOverride = getBooleanOrDefault("mobs.piglin.mob-griefing-override", piglinMobGriefingOverride);
piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater);
piglinPortalSpawnModifier = getInt("mobs.piglin.portal-spawn-modifier", piglinPortalSpawnModifier);
+ // Leaf start - Fix Pufferfish and Purpur patches - better input sanitization
+ if (piglinPortalSpawnModifier < 1) {
+ piglinPortalSpawnModifier = 1;
+ log(Level.WARNING, "mobs.piglin.portal-spawn-modifier is set to below minimum allowed value of 1");
+ log(Level.WARNING, "Using value of 1 to prevent issues");
+ }
+ // Leaf end - Fix Pufferfish and Purpur patches - better input sanitization
piglinAlwaysDropExp = getBoolean("mobs.piglin.always-drop-exp", piglinAlwaysDropExp);
piglinHeadVisibilityPercent = getDouble("mobs.piglin.head-visibility-percent", piglinHeadVisibilityPercent);
piglinIgnoresArmorWithGoldTrim = getBoolean("mobs.piglin.ignores-armor-with-gold-trim", piglinIgnoresArmorWithGoldTrim);

View File

@@ -19,7 +19,7 @@ This patch was ported downstream from the Petal fork.
Makes most pathfinding-related work happen asynchronously
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 33c99f3b24596fbbf3283b455ea8bf340203e01b..e9ebf23b0b1af85e3738a70acc0eaa3e8980261f 100644
index fbcf26c0bfb9f496c99bd5a2ba988be06162cd52..cf136bc3d0d285ebde23c6e31c002933564fdcb2 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -243,6 +243,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
@@ -560,19 +560,20 @@ index 1f96fd5085bacb4c584576c7cb9f51e7898e9b03..03b6c8c8dcd42e864751e68be9d35d20
+ // Leaf end - Kaiiju - await on async path processing
}
diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java
index e9dfff7e3726cd2229f89bb39fa1ca4815d99a6d..654ecd20ef4a43f7ffc447c15434ce46ab89efe9 100644
index e9dfff7e3726cd2229f89bb39fa1ca4815d99a6d..04c0dad21c4cdd464a8ebe830bcdd217bb4156ec 100644
--- a/net/minecraft/world/entity/animal/Bee.java
+++ b/net/minecraft/world/entity/animal/Bee.java
@@ -936,7 +936,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@@ -936,7 +936,8 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
} else {
Bee.this.pathfindRandomlyTowards(Bee.this.hivePos);
}
- } else {
+ } else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // Kaiiju - petal - check processing
+ //} else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // Kaiiju - petal - check processing // todo
+ } else {
boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos);
if (!flag) {
this.dropAndBlacklistHive();
@@ -990,7 +990,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@@ -990,7 +991,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
return true;
} else {
Path path = Bee.this.navigation.getPath();

View File

@@ -83,7 +83,7 @@ index 39489c8a347031fb4f73faca46039786e35762ac..4de1d0966157b20526386becee10b9be
}
diff --git a/org/purpurmc/purpur/PurpurWorldConfig.java b/org/purpurmc/purpur/PurpurWorldConfig.java
index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552c53a42c5 100644
index cadfbb7310fce33eda24d69c39fda5689c7fb882..21765347d7a81f4111f23685f699286d5e5cccb6 100644
--- a/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -1625,6 +1625,7 @@ public class PurpurWorldConfig {
@@ -118,7 +118,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
}
public boolean illusionerRidable = false;
@@ -3417,6 +3421,7 @@ public class PurpurWorldConfig {
@@ -3424,6 +3428,7 @@ public class PurpurWorldConfig {
public boolean zombieTakeDamageFromWater = false;
public boolean zombieAlwaysDropExp = false;
public double zombieHeadVisibilityPercent = 0.5D;
@@ -126,7 +126,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
private void zombieSettings() {
zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable);
zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater);
@@ -3442,6 +3447,7 @@ public class PurpurWorldConfig {
@@ -3449,6 +3454,7 @@ public class PurpurWorldConfig {
zombieTakeDamageFromWater = getBoolean("mobs.zombie.takes-damage-from-water", zombieTakeDamageFromWater);
zombieAlwaysDropExp = getBoolean("mobs.zombie.always-drop-exp", zombieAlwaysDropExp);
zombieHeadVisibilityPercent = getDouble("mobs.zombie.head-visibility-percent", zombieHeadVisibilityPercent);
@@ -134,7 +134,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
}
public boolean zombieHorseRidable = false;
@@ -3491,6 +3497,7 @@ public class PurpurWorldConfig {
@@ -3498,6 +3504,7 @@ public class PurpurWorldConfig {
public int zombieVillagerCuringTimeMax = 6000;
public boolean zombieVillagerCureEnabled = true;
public boolean zombieVillagerAlwaysDropExp = false;
@@ -142,7 +142,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
private void zombieVillagerSettings() {
zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable);
zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater);
@@ -3511,6 +3518,7 @@ public class PurpurWorldConfig {
@@ -3518,6 +3525,7 @@ public class PurpurWorldConfig {
zombieVillagerCuringTimeMax = getInt("mobs.zombie_villager.curing_time.max", zombieVillagerCuringTimeMax);
zombieVillagerCureEnabled = getBoolean("mobs.zombie_villager.cure.enabled", zombieVillagerCureEnabled);
zombieVillagerAlwaysDropExp = getBoolean("mobs.zombie_villager.always-drop-exp", zombieVillagerAlwaysDropExp);
@@ -150,7 +150,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
}
public boolean zombifiedPiglinRidable = false;
@@ -3525,6 +3533,7 @@ public class PurpurWorldConfig {
@@ -3532,6 +3540,7 @@ public class PurpurWorldConfig {
public boolean zombifiedPiglinCountAsPlayerKillWhenAngry = false;
public boolean zombifiedPiglinTakeDamageFromWater = false;
public boolean zombifiedPiglinAlwaysDropExp = false;
@@ -158,7 +158,7 @@ index 31cc4e6e68a9743aaaeb296cb31e64526d3f1f73..67739ba7cb82d615a5f0c4db7aaa5552
private void zombifiedPiglinSettings() {
zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable);
zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater);
@@ -3546,6 +3555,7 @@ public class PurpurWorldConfig {
@@ -3553,6 +3562,7 @@ public class PurpurWorldConfig {
zombifiedPiglinCountAsPlayerKillWhenAngry = getBoolean("mobs.zombified_piglin.count-as-player-kill-when-angry", zombifiedPiglinCountAsPlayerKillWhenAngry);
zombifiedPiglinTakeDamageFromWater = getBoolean("mobs.zombified_piglin.takes-damage-from-water", zombifiedPiglinTakeDamageFromWater);
zombifiedPiglinAlwaysDropExp = getBoolean("mobs.zombified_piglin.always-drop-exp", zombifiedPiglinAlwaysDropExp);

View File

@@ -263,7 +263,7 @@ index 234f123959830cc2adb78b9dc8752906140e5b11..e0dceff32b47b334ddcb76271e3cf3ea
org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27);
enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows);
diff --git a/org/purpurmc/purpur/PurpurWorldConfig.java b/org/purpurmc/purpur/PurpurWorldConfig.java
index 67739ba7cb82d615a5f0c4db7aaa5552c53a42c5..d81e06592aa9521f866c2c910b43a7364de3796e 100644
index 21765347d7a81f4111f23685f699286d5e5cccb6..8459f5b9bf548e51b85e753a4e65dd86baa2b5df 100644
--- a/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -1189,12 +1189,20 @@ public class PurpurWorldConfig {
@@ -353,7 +353,7 @@ index 67739ba7cb82d615a5f0c4db7aaa5552c53a42c5..d81e06592aa9521f866c2c910b43a736
}
public boolean ghastRidable = false;
@@ -2911,6 +2945,10 @@ public class PurpurWorldConfig {
@@ -2918,6 +2952,10 @@ public class PurpurWorldConfig {
public double snifferMaxHealth = 14.0D;
public double snifferScale = 1.0D;
public int snifferBreedingTicks = 6000;
@@ -364,7 +364,7 @@ index 67739ba7cb82d615a5f0c4db7aaa5552c53a42c5..d81e06592aa9521f866c2c910b43a736
private void snifferSettings() {
snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable);
snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater);
@@ -2918,6 +2956,10 @@ public class PurpurWorldConfig {
@@ -2925,6 +2963,10 @@ public class PurpurWorldConfig {
snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth);
snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D);
snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", snifferBreedingTicks);
@@ -375,7 +375,7 @@ index 67739ba7cb82d615a5f0c4db7aaa5552c53a42c5..d81e06592aa9521f866c2c910b43a736
}
public boolean squidRidable = false;
@@ -3019,10 +3061,20 @@ public class PurpurWorldConfig {
@@ -3026,10 +3068,20 @@ public class PurpurWorldConfig {
public boolean tadpoleRidable = false;
public boolean tadpoleRidableInWater = true;
public boolean tadpoleControllable = true;
@@ -396,7 +396,7 @@ index 67739ba7cb82d615a5f0c4db7aaa5552c53a42c5..d81e06592aa9521f866c2c910b43a736
}
public boolean traderLlamaRidable = false;
@@ -3256,10 +3308,20 @@ public class PurpurWorldConfig {
@@ -3263,10 +3315,20 @@ public class PurpurWorldConfig {
public boolean wardenRidable = false;
public boolean wardenRidableInWater = true;
public boolean wardenControllable = true;

View File

@@ -34,7 +34,7 @@ index f861f9e087182470a3bbb22678dbdacb8a73e943..a3d0d17178eedfaef83e2e0df6b1c2d7
private DensityFunction wrapNew(DensityFunction densityFunction) {
diff --git a/net/minecraft/world/level/levelgen/SurfaceRules.java b/net/minecraft/world/level/levelgen/SurfaceRules.java
index 0948c8db90605a15a043b5c5bc74edecd7f9db1b..009e8a270c25614d03413d8b8b1f39c2da8ba12f 100644
index 0948c8db90605a15a043b5c5bc74edecd7f9db1b..6cba88415a4715527e163e54662db9b3ab37c747 100644
--- a/net/minecraft/world/level/levelgen/SurfaceRules.java
+++ b/net/minecraft/world/level/levelgen/SurfaceRules.java
@@ -313,8 +313,15 @@ public class SurfaceRules {
@@ -48,9 +48,9 @@ index 0948c8db90605a15a043b5c5bc74edecd7f9db1b..009e8a270c25614d03413d8b8b1f39c2
+ ++this.lastUpdateY;
+ Supplier<Holder<Biome>> getter = this.biome;
+ if (getter == null) {
+ this.biome = getter = new org.dreeam.leaf.util.biome.PositionalBiomeGetter(this.biomeGetter, this.pos);
+ this.biome = getter = new org.dreeam.leaf.world.biome.PositionalBiomeGetter(this.biomeGetter, this.pos);
+ }
+ ((org.dreeam.leaf.util.biome.PositionalBiomeGetter) getter).update(blockX, blockY, blockZ);
+ ((org.dreeam.leaf.world.biome.PositionalBiomeGetter) getter).update(blockX, blockY, blockZ);
+ // Leaf end - Reduce worldgen allocations
this.blockY = blockY;
this.waterHeight = waterHeight;

View File

@@ -23,6 +23,28 @@ for the case of some NPC plugins which using real entity type, e.g. Citizens.
But it is still recommending to use those packet based, virtual entity
based NPC plugins, e.g. ZNPC Plus, Adyeshach, Fancy NPC, etc.
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..4200d22606c6a3dbdf282792a4007a51df66963b 100644
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
@@ -60,7 +60,16 @@ public final class NearbyPlayers {
private final ServerLevel world;
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
+ // Leaf start - Multithreaded tracker
+ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap<TrackedChunk> byChunk;
+ {
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ byChunk = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>());
+ } else {
+ byChunk = new Long2ReferenceOpenHashMap<>();
+ }
+ }
+ // Leaf end - Multithreaded tracker
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
{
for (int i = 0; i < this.directByChunk.length; ++i) {
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index 02a9ef1694c796584c29430d27f0a09047368835..32608df3da169159c070f37cb55407f4f6187744 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -36,8 +58,20 @@ index 02a9ef1694c796584c29430d27f0a09047368835..32608df3da169159c070f37cb55407f4
private static final byte CHUNK_TICKET_STAGE_NONE = 0;
private static final byte CHUNK_TICKET_STAGE_LOADING = 1;
diff --git a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
index 9c0c99b936b4a82ebfe924866e53ec71f7bbe9ad..2ccff968cb2065d34fad4d27573f9e3081edb2f2 100644
--- a/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
+++ b/net/minecraft/network/protocol/game/ClientboundUpdateAttributesPacket.java
@@ -32,6 +32,7 @@ public class ClientboundUpdateAttributesPacket implements Packet<ClientGamePacke
this.attributes = Lists.newArrayList();
for (AttributeInstance attributeInstance : attributes) {
+ if (attributeInstance == null) continue; // Leaf - Multithreaded tracker
this.attributes
.add(
new ClientboundUpdateAttributesPacket.AttributeSnapshot(
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e693645dfe 100644
index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..ae0a7c9b95f4f2561769e0d661fadbe29a2d6f8b 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -255,6 +255,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -70,14 +104,13 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
// Paper start - optimise entity tracker
if (true) {
this.newTrackerTick();
@@ -1135,7 +1151,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1135,7 +1151,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
+ // Leaf start - Multithreaded tracker
+ public static final ServerPlayerConnection[] EMPTY_OBJECT_ARRAY = new ServerPlayerConnection[0];
+ public final Object sync = new Object();
+ public final Set<ServerPlayerConnection> seenBy = org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled ? it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>()) : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl
+ private volatile ServerPlayerConnection[] seenByArray = EMPTY_OBJECT_ARRAY;
+ public ServerPlayerConnection[] seenBy() {
@@ -90,7 +123,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
// Paper start - optimise entity tracker
private long lastChunkUpdate = -1L;
@@ -1162,27 +1189,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1162,27 +1188,95 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastTrackedChunk = chunk;
final ServerPlayer[] playersRaw = players.getRawDataUnchecked();
@@ -191,7 +224,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(conn.getPlayer())) {
foundToRemove = true;
break;
@@ -1193,12 +1288,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1193,12 +1287,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return;
}
@@ -207,7 +240,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
}
@Override
@@ -1208,10 +1304,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1208,10 +1303,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (this.seenBy.isEmpty()) {
return;
}
@@ -221,7 +254,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
}
@Override
@@ -1238,7 +1335,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1238,7 +1334,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcast(Packet<?> packet) {
@@ -230,7 +263,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
serverPlayerConnection.send(packet);
}
}
@@ -1259,21 +1356,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1259,21 +1355,34 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcastRemoved() {
@@ -268,7 +301,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
// Paper start - remove allocation of Vec3D here
// Vec3 vec3 = player.position().subtract(this.entity.position());
double vec3_dx = player.getX() - this.entity.getX();
@@ -1301,6 +1411,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1301,6 +1410,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
@@ -276,7 +309,7 @@ index c60b9e4076450de2157c1a3cf4f98cc2c19e4e6a..41da66fd77924a9a4bd9cc12f517e2e6
// Paper start - entity tracking events
if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) {
this.serverEntity.addPairing(player);
@@ -1309,6 +1420,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1309,6 +1419,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.serverEntity.onPlayerAdd(); // Paper - fix desync when a player is added to the tracker
}
} else if (this.seenBy.remove(player.connection)) {
@@ -298,18 +331,10 @@ index f106373ef3ac4a8685c2939c9e8361688a285913..51ae390c68e7a3aa193329cc3bc47ca6
public boolean visible = true;
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 1a9601aee097b6c10cf2ae1c52fddf45da85f60f..867936866d952c559b6ffa49fdf78acd70a9bab9 100644
index 1a9601aee097b6c10cf2ae1c52fddf45da85f60f..16b2ca8c96e9561aa57e0903d1e98e6441044b6d 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -75,6 +75,7 @@ public class ServerEntity {
@Nullable
private List<SynchedEntityData.DataValue<?>> trackedDataValues;
private final Set<net.minecraft.server.network.ServerPlayerConnection> trackedPlayers; // Paper
+ public boolean wantSendDirtyEntityData = false; // Leaf - Multithreaded tracker
public ServerEntity(
ServerLevel level,
@@ -146,7 +147,7 @@ public class ServerEntity {
@@ -146,7 +146,7 @@ public class ServerEntity {
MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames
MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level);
if (savedData != null) {
@@ -318,21 +343,17 @@ index 1a9601aee097b6c10cf2ae1c52fddf45da85f60f..867936866d952c559b6ffa49fdf78acd
final ServerPlayer serverPlayer = connection.getPlayer(); // Paper
savedData.tickCarriedBy(serverPlayer, item);
Packet<?> updatePacket = savedData.getUpdatePacket(mapId, serverPlayer);
@@ -450,6 +451,12 @@ public class ServerEntity {
@@ -468,7 +468,7 @@ public class ServerEntity {
this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync));
}
- attributesToSync.clear();
+ // attributesToSync.clear(); // Leaf - Multithreaded tracker
}
}
public void sendDirtyEntityData() {
+ // Leaf start - Multithreaded tracker
+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.tracker.MultithreadedTracker.MultithreadedTrackerThread) {
+ wantSendDirtyEntityData = true;
+ return;
+ }
+ // Leaf end - Multithreaded tracker
SynchedEntityData entityData = this.entity.getEntityData();
List<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
if (list != null) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce 100644
index 42f56bd6a9f449df23f7e7f00b3efa114a003449..0471b04833fbca5dfc0cc6575692cdefb83edbed 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
@@ -354,7 +375,7 @@ index 5943b18f172fb1d77ef1fe768daa8e8f43c3c8c1..7b85a9ebdbe3e8bee0a8fc100ede8a3f
}
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 729f595491c7a4edf24dff2e876dfb69ade87a17..a3069b29a1b78012314747d705e27c167acd10b3 100644
index be803fd0a79850254bd4a7f64534142cd5b4ec98..6992a53abdcff6299bedf33ba0b1bc6386a1ea74 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1878,7 +1878,7 @@ public class ServerGamePacketListenerImpl
@@ -366,6 +387,233 @@ index 729f595491c7a4edf24dff2e876dfb69ade87a17..a3069b29a1b78012314747d705e27c16
// Paper start - Prevent teleporting dead entities
if (this.player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 4c3eadc2d8480b2a2c2c08e58620544d403d3adc..68241d1d488bc2848e3c0d167270c1788e573c37 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1317,13 +1317,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
private void refreshDirtyAttributes() {
- Set<AttributeInstance> attributesToUpdate = this.getAttributes().getAttributesToUpdate();
+ // Leaf start - Multithreaded tracker
+ int[] attributesToUpdate = this.getAttributes().getAttributesToUpdateIds();
- for (AttributeInstance attributeInstance : attributesToUpdate) {
- this.onAttributeUpdated(attributeInstance.getAttribute());
+ for (int attribute : attributesToUpdate) {
+ this.onAttributeUpdated(net.minecraft.core.registries.BuiltInRegistries.ATTRIBUTE.get(attribute).orElseThrow());
}
-
- attributesToUpdate.clear();
+ // Leaf end - Multithreaded tracker
}
protected void onAttributeUpdated(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
@@ -16,10 +16,15 @@ public class Attribute {
private boolean syncable;
private final String descriptionId;
private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
+ // Leaf start - Optimize AttributeMap
+ public final int uid;
+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger();
+ // Leaf end - Optimize AttributeMap
protected Attribute(String descriptionId, double defaultValue) {
this.defaultValue = defaultValue;
this.descriptionId = descriptionId;
+ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap
}
public double getDefaultValue() {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..9f09d78a7dac12c7f1b06029d32ad93fae0c2aec 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -26,8 +26,24 @@ public class AttributeInstance {
private final Map<AttributeModifier.Operation, Map<ResourceLocation, AttributeModifier>> modifiersByOperation = Maps.newEnumMap(
AttributeModifier.Operation.class
);
- private final Map<ResourceLocation, AttributeModifier> modifierById = new Object2ObjectArrayMap<>();
- private final Map<ResourceLocation, AttributeModifier> permanentModifiers = new Object2ObjectArrayMap<>();
+ // Leaf start - Multithreaded tracker
+ private final Map<ResourceLocation, AttributeModifier> modifierById;
+ {
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ modifierById = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ modifierById = new Object2ObjectArrayMap<>();
+ }
+ }
+ private final Map<ResourceLocation, AttributeModifier> permanentModifiers;
+ {
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ permanentModifiers = it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ } else {
+ permanentModifiers = new Object2ObjectArrayMap<>();
+ }
+ }
+ // Leaf end - Multithreaded tracker
private double baseValue;
private boolean dirty = true;
private double cachedValue;
@@ -56,7 +72,13 @@ public class AttributeInstance {
@VisibleForTesting
Map<ResourceLocation, AttributeModifier> getModifiers(AttributeModifier.Operation operation) {
- return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> new Object2ObjectOpenHashMap<>());
+ // Leaf start - Multithreaded tracker
+ return this.modifiersByOperation.computeIfAbsent(operation, operation1 -> {
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled)
+ return it.unimi.dsi.fastutil.objects.Object2ObjectMaps.synchronize(new Object2ObjectArrayMap<>(), this);
+ else return new Object2ObjectArrayMap<>();
+ });
+ // Leaf end - Multithreaded tracker
}
public Set<AttributeModifier> getModifiers() {
@@ -144,8 +166,12 @@ public class AttributeInstance {
public double getValue() {
if (this.dirty) {
- this.cachedValue = this.calculateValue();
+ // Leaf start - Multithreaded tracker
+ double value = this.calculateValue();
+ this.cachedValue = value;
this.dirty = false;
+ return value;
+ // Leaf end - Multithreaded tracker
}
return this.cachedValue;
@@ -192,7 +218,15 @@ public class AttributeInstance {
compoundTag.store("id", TYPE_CODEC, this.attribute);
compoundTag.putDouble("base", this.baseValue);
if (!this.permanentModifiers.isEmpty()) {
- compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
+ // Leaf start - Multithreaded tracker
+ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) {
+ synchronized (this) {
+ compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
+ }
+ } else {
+ compoundTag.store("modifiers", AttributeModifier.CODEC.listOf(), List.copyOf(this.permanentModifiers.values()));
+ }
+ // Leaf end - Multithreaded tracker
}
return compoundTag;
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..778872cdd1717f1ad52de32faf43847a08c55269 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -15,12 +15,14 @@ import net.minecraft.resources.ResourceLocation;
public class AttributeMap {
// Gale start - Lithium - replace AI attributes with optimized collections
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
- private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
- private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
+ // Leaf start - Multithreaded tracker
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap();
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToSync = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
+ private final org.dreeam.leaf.util.map.AttributeInstanceSet attributesToUpdate = new org.dreeam.leaf.util.map.AttributeInstanceSet((org.dreeam.leaf.util.map.AttributeInstanceArrayMap) attributes);
+ // Leaf end - Multithreaded tracker
// Gale end - Lithium - replace AI attributes with optimized collections
private final AttributeSupplier supplier;
- private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
+ //private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
public AttributeMap(AttributeSupplier supplier) {
@@ -31,31 +33,54 @@ public class AttributeMap {
this.entity = entity;
// Purpur end - Ridables
this.supplier = defaultAttributes;
- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
+ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
}
- private void onAttributeModified(AttributeInstance instance) {
+ // Leaf start - Multithreaded tracker
+ private synchronized void onAttributeModified(AttributeInstance instance) {
this.attributesToUpdate.add(instance);
if (instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))) { // Purpur - Ridables
this.attributesToSync.add(instance);
}
}
- public Set<AttributeInstance> getAttributesToSync() {
- return this.attributesToSync;
+ private static final AttributeInstance[] EMPTY_ATTRIBUTE_INSTANCE = new AttributeInstance[0];
+ public synchronized Set<AttributeInstance> getAttributesToSync() {
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToSync.toArray(EMPTY_ATTRIBUTE_INSTANCE));
+ this.attributesToSync.clear();
+ return clone;
}
- public Set<AttributeInstance> getAttributesToUpdate() {
- return this.attributesToUpdate;
+ public synchronized Set<AttributeInstance> getAttributesToUpdate() {
+ var clone = it.unimi.dsi.fastutil.objects.ReferenceArraySet.ofUnchecked(attributesToUpdate.toArray(EMPTY_ATTRIBUTE_INSTANCE));
+ this.attributesToUpdate.clear();
+ return clone;
}
+ public synchronized int[] getAttributesToUpdateIds() {
+ int[] clone = attributesToUpdate.inner.toIntArray();
+ this.attributesToUpdate.clear();
+ return clone;
+ }
+ // Leaf end - Multithreaded tracker
+
public Collection<AttributeInstance> getSyncableAttributes() {
return this.attributes.values().stream().filter(instance -> instance.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(instance.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables
}
@Nullable
public AttributeInstance getInstance(Holder<Attribute> attribute) {
- return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways
+ // Leaf start - Multithreaded tracker
+ AttributeInstance v;
+ if ((v = this.attributes.get(attribute)) == null) {
+ AttributeInstance newValue;
+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) {
+ attributes.put(attribute, newValue);
+ return newValue;
+ }
+ }
+ return v;
+ // Leaf end - Multithreaded tracker
}
public boolean hasAttribute(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
index 24710041ccbc70e5506d8d89ae34f0141977f209..05de8a77b389691dd6986f36b4cb8cc0935e21e4 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
@@ -11,7 +11,7 @@ public class AttributeSupplier {
private final Map<Holder<Attribute>, AttributeInstance> instances;
AttributeSupplier(Map<Holder<Attribute>, AttributeInstance> instances) {
- this.instances = instances;
+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap
}
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {
@@ -41,7 +41,7 @@ public class AttributeSupplier {
}
@Nullable
- public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) {
+ public AttributeInstance createInstance(Consumer<AttributeInstance> onDirty, Holder<Attribute> attribute) { // Leaf - Multithreaded tracker
AttributeInstance attributeInstance = this.instances.get(attribute);
if (attributeInstance == null) {
return null;
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
index 36e3937c9e09852937c94c268c877a15337835c5..8aedc3ca463745fe32cac977208b23dc0b8e73b6 100644
--- a/net/minecraft/world/entity/item/PrimedTnt.java

View File

@@ -9,10 +9,10 @@ This patch didn't cahce SectionPos or BlockPos to chunkKey, since it needs to co
TODO: Cache block pos and section pos, whether need?
diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..288a3eb57f3431dd624ad8a4b08684563abbc5ad 100644
index 4200d22606c6a3dbdf282792a4007a51df66963b..4b258f048c73107d0d050a9aa4b4a39788145b17 100644
--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
@@ -127,7 +127,7 @@ public final class NearbyPlayers {
@@ -136,7 +136,7 @@ public final class NearbyPlayers {
}
public TrackedChunk getChunk(final ChunkPos pos) {
@@ -21,7 +21,7 @@ index 1b8193587814225c2ef2c5d9e667436eb50ff6c5..288a3eb57f3431dd624ad8a4b0868456
}
public TrackedChunk getChunk(final BlockPos pos) {
@@ -143,7 +143,7 @@ public final class NearbyPlayers {
@@ -152,7 +152,7 @@ public final class NearbyPlayers {
}
public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
@@ -97,7 +97,7 @@ index fd3d0f6cb53bc8b6186f0d86575f21007b2c20ed..cddeeab73e7b981701a42c5aad6b4777
// Paper end - rewrite chunk system
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 7b85a9ebdbe3e8bee0a8fc100ede8a3f07eee5ce..c8c99323b6397c3e595e7a9007e5d801ee2ac14a 100644
index 0471b04833fbca5dfc0cc6575692cdefb83edbed..1f3020f5a051d5f4e5c8fa088f844962c3105573 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -503,7 +503,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe

View File

@@ -8,11 +8,26 @@ Paper: ~75ms
Leaf: ~48ms (-36%)
This should help drastically on the farms that use actively changing fluids.
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index c6eb136e8db0aa232681ac9bd28c4b70fbbacb40..706e198631e4a3b0a5d2fc3f58060e4a15701ecd 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1300,6 +1300,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.emptyTime = 0;
}
+ // Leaf start - Use BFS on getSlopeDistance
+ public it.unimi.dsi.fastutil.longs.LongSet slopeDistanceCacheVisited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512);
+ public net.minecraft.world.level.material.FlowingFluid.SlopeDistanceNodeDeque slopeDistanceCacheQueue = new net.minecraft.world.level.material.FlowingFluid.SlopeDistanceNodeDeque();
+ // Leaf end - Use BFS on getSlopeDistance
private void tickFluid(BlockPos pos, Fluid fluid) {
BlockState blockState = this.getBlockState(pos);
FluidState fluidState = blockState.getFluidState();
diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java
index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..c0f78556d112e59333ace60f1522e7fd1efe71c3 100644
index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..a1c6a30c1e446955b409d1f00c602db06c1e9813 100644
--- a/net/minecraft/world/level/material/FlowingFluid.java
+++ b/net/minecraft/world/level/material/FlowingFluid.java
@@ -342,32 +342,81 @@ public abstract class FlowingFluid extends Fluid {
@@ -342,32 +342,124 @@ public abstract class FlowingFluid extends Fluid {
protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state);
@@ -20,13 +35,15 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..c0f78556d112e59333ace60f1522e7fd
- 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);
+ java.util.Queue<FlowingFluid.SlopeDistanceNode> queue = new java.util.ArrayDeque<>(256);
+ it.unimi.dsi.fastutil.longs.LongSet visited = ((ServerLevel) level).slopeDistanceCacheVisited;
+ SlopeDistanceNodeDeque queue = ((ServerLevel) level).slopeDistanceCacheQueue;
+ visited.clear();
+ queue.clear();
+
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
+ if (dir == excludedDirection) continue;
+
+ BlockPos neighborPos = startPos.relative(dir);
+ BlockPos neighborPos = startPos.relative(dir); // immutable
+ BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos);
+ if (neighborState == null) continue;
+
@@ -72,7 +89,7 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..c0f78556d112e59333ace60f1522e7fd
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
+ if (dir == current.excludedDir) continue;
+
+ BlockPos nextPos = current.pos.relative(dir);
+ BlockPos nextPos = current.pos.relative(dir); // immutable
+ BlockState nextState = spreadContext.getBlockStateIfLoaded(nextPos);
+ if (nextState == null) continue;
+
@@ -96,18 +113,59 @@ index 4625bd55e1cb01dfb9921dcd033f05b4a8f9ad74..c0f78556d112e59333ace60f1522e7fd
+ return ((long) pos.getX() & 0xFFFFFFFFL) << 32 | ((long) pos.getZ() & 0xFFFFFFFFL) << 4 | (excludedDir.ordinal() & 0x0F);
+ }
+
+ private static class SlopeDistanceNode {
+ final BlockPos pos;
+ final int depth;
+ final Direction excludedDir;
+ final BlockState state;
+ public static class SlopeDistanceNodeDeque {
+ private SlopeDistanceNode[] array;
+ private int length;
+ private int start;
+ private int end;
+
+ SlopeDistanceNode(BlockPos pos, int depth, Direction excludedDir, BlockState state) {
+ this.pos = pos.immutable();
+ this.depth = depth;
+ this.excludedDir = excludedDir;
+ this.state = state;
+ public SlopeDistanceNodeDeque() {
+ array = new SlopeDistanceNode[256];
+ length = array.length;
+ }
+
+ /*
+ private int size() {
+ int apparent = end - start;
+ return apparent >= 0 ? apparent : length + apparent;
+ }
+ */
+
+ private void clear() {
+ start = 0;
+ end = 0;
+ }
+
+ private boolean isEmpty() {
+ return end == start || (end <= start && length == start - end);
+ }
+
+ private SlopeDistanceNode poll() {
+ final SlopeDistanceNode t = array[start];
+ if (++start == length) start = 0;
+ return t;
+ }
+
+ private void add(final SlopeDistanceNode node) {
+ array[end++] = node;
+ if (end == length) end = 0;
+ if (end == start) resize(length, 2 * length);
+ }
+
+ private void resize(final int size, final int newLength) {
+ final SlopeDistanceNode[] newArray = new SlopeDistanceNode[newLength];
+ if (size != 0) {
+ System.arraycopy(array, start, newArray, 0, length - start);
+ System.arraycopy(array, 0, newArray, length - start, end);
+ }
+ start = 0;
+ end = size;
+ array = newArray;
+ length = newLength;
+ }
+ }
+
+ private record SlopeDistanceNode(BlockPos pos, int depth, Direction excludedDir, BlockState state) {
}
+ // Leaf end - Use BFS on getSlopeDistance

View File

@@ -5,10 +5,10 @@ Subject: [PATCH] Optimize addOrUpdateTransientModifier
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
index 3ac9f36eae87369354e992a1d9b5c5b2d87d17cb..c39654d3e1e5573646b3729502e4eae1a6dba7c9 100644
index 9f09d78a7dac12c7f1b06029d32ad93fae0c2aec..2af5cd6f2b9541cb28075fda014350235a4f72c7 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java
@@ -87,8 +87,13 @@ public class AttributeInstance {
@@ -109,8 +109,13 @@ public class AttributeInstance {
}
public void addOrUpdateTransientModifier(AttributeModifier modifier) {

View File

@@ -149,7 +149,7 @@ index 54910c2e1d6e6bb556e536fda060bd09402e04e8..747eb54f84650a9a507398e3d5352e00
// Gale start - Pufferfish - SIMD support
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 1346cff0018e6d17cb892892c3aeebfb86453b60..a753481afe02a2367c378c7f39206fde23eda00d 100644
index 1d2c27a7595701895aa3c96339814fd1454a50dd..db3bff36c812a00fc35ebcc6ac6d11ce24040003 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -173,7 +173,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -220,7 +220,7 @@ index 1346cff0018e6d17cb892892c3aeebfb86453b60..a753481afe02a2367c378c7f39206fde
}
// Paper - rewrite chunk system
}
@@ -1312,6 +1337,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1316,6 +1341,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper end - rewrite chunk system
@@ -228,7 +228,7 @@ index 1346cff0018e6d17cb892892c3aeebfb86453b60..a753481afe02a2367c378c7f39206fde
}
private void tickBlock(BlockPos pos, Block block) {
@@ -1325,6 +1351,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1329,6 +1355,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
// Paper end - rewrite chunk system

View File

@@ -35,10 +35,10 @@ index 78aee57ad8224b0728411c699d2e3844847c9c79..8f5b400bdf5c1c194f75ee98e2f1e984
this.tickables.get(i).run();
}
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
index 867936866d952c559b6ffa49fdf78acd70a9bab9..b1953701583ba88c524368a52988fea6fb1ab8d2 100644
index 16b2ca8c96e9561aa57e0903d1e98e6441044b6d..939400c18eb4e87e0bf1b131e1601f4dcaa4885c 100644
--- a/net/minecraft/server/level/ServerEntity.java
+++ b/net/minecraft/server/level/ServerEntity.java
@@ -298,6 +298,7 @@ public class ServerEntity {
@@ -297,6 +297,7 @@ public class ServerEntity {
this.entity.hurtMarked = false;
this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity));
}

View File

@@ -1,86 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hayanesuru <hayanesuru@outlook.jp>
Date: Thu, 15 May 2025 21:11:18 +0900
Subject: [PATCH] Optimize AttributeMap
diff --git a/net/minecraft/world/entity/ai/attributes/Attribute.java b/net/minecraft/world/entity/ai/attributes/Attribute.java
index f8419dde44ebc7324e783f8bee42132d5ec973c3..406767c60ec1a324faaf5d3658b161647497f99b 100644
--- a/net/minecraft/world/entity/ai/attributes/Attribute.java
+++ b/net/minecraft/world/entity/ai/attributes/Attribute.java
@@ -16,10 +16,15 @@ public class Attribute {
private boolean syncable;
private final String descriptionId;
private Attribute.Sentiment sentiment = Attribute.Sentiment.POSITIVE;
+ // Leaf start - Optimize AttributeMap
+ public final int uid;
+ private static final java.util.concurrent.atomic.AtomicInteger SIZE = new java.util.concurrent.atomic.AtomicInteger();
+ // Leaf end - Optimize AttributeMap
protected Attribute(String descriptionId, double defaultValue) {
this.defaultValue = defaultValue;
this.descriptionId = descriptionId;
+ this.uid = SIZE.getAndAdd(1); // Leaf - Optimize AttributeMap
}
public double getDefaultValue() {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
index 701025715e0aca3c1f920a66f9b3d03ec08eaf02..1ef934b02bd99c0fe997e9ec163457b39dba55f0 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java
@@ -15,12 +15,12 @@ import net.minecraft.resources.ResourceLocation;
public class AttributeMap {
// Gale start - Lithium - replace AI attributes with optimized collections
- private final Map<Holder<Attribute>, AttributeInstance> attributes = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0);
+ private final Map<Holder<Attribute>, AttributeInstance> attributes = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(); // Leaf - Optimize AttributeMap
private final Set<AttributeInstance> attributesToSync = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
private final Set<AttributeInstance> attributesToUpdate = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0);
// Gale end - Lithium - replace AI attributes with optimized collections
private final AttributeSupplier supplier;
- private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations
+ //private final java.util.function.Function<Holder<Attribute>, AttributeInstance> createInstance; // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables
public AttributeMap(AttributeSupplier supplier) {
@@ -31,7 +31,7 @@ public class AttributeMap {
this.entity = entity;
// Purpur end - Ridables
this.supplier = defaultAttributes;
- this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations
+ //this.createInstance = holder -> this.supplier.createInstance(this::onAttributeModified, holder); // Gale - Airplane - reduce entity allocations // Leaf - Optimize AttributeMap
}
private void onAttributeModified(AttributeInstance instance) {
@@ -55,7 +55,17 @@ public class AttributeMap {
@Nullable
public AttributeInstance getInstance(Holder<Attribute> attribute) {
- return this.attributes.computeIfAbsent(attribute, this.createInstance); // Gale - Airplane - reduce entity allocations - cache lambda, as for some reason java allocates it anyways
+ // Leaf start - Optimize AttributeMap
+ AttributeInstance v;
+ if ((v = this.attributes.get(attribute)) == null) {
+ AttributeInstance newValue;
+ if ((newValue = this.supplier.createInstance(this::onAttributeModified, attribute)) != null) {
+ this.attributes.put(attribute, newValue);
+ return newValue;
+ }
+ }
+ return v;
+ // Leaf end - Optimize AttributeMap
}
public boolean hasAttribute(Holder<Attribute> attribute) {
diff --git a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
index 24710041ccbc70e5506d8d89ae34f0141977f209..09341ef6c651150aba223689badbead490162b2b 100644
--- a/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
+++ b/net/minecraft/world/entity/ai/attributes/AttributeSupplier.java
@@ -11,7 +11,7 @@ public class AttributeSupplier {
private final Map<Holder<Attribute>, AttributeInstance> instances;
AttributeSupplier(Map<Holder<Attribute>, AttributeInstance> instances) {
- this.instances = instances;
+ this.instances = new org.dreeam.leaf.util.map.AttributeInstanceArrayMap(instances); // Leaf - Optimize AttributeMap
}
public AttributeInstance getAttributeInstance(Holder<Attribute> attribute) {

View File

@@ -94,10 +94,10 @@ index 0f9d18dd29e210ad656da211a3cb1cb25cd4efb1..d1c36cd17c83e7e0167046093c4a2b84
for (LevelChunk levelChunk : list) {
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a753481afe02a2367c378c7f39206fde23eda00d..753b4255d9e9b9628bc5825a6b4f0f2540f06e7a 100644
index db3bff36c812a00fc35ebcc6ac6d11ce24040003..e8706189de80a2f0e60882fff0fe891165050f38 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1367,13 +1367,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1371,13 +1371,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper end - log detailed entity tick information
public void tickNonPassenger(Entity entity) {
@@ -111,7 +111,7 @@ index a753481afe02a2367c378c7f39206fde23eda00d..753b4255d9e9b9628bc5825a6b4f0f25
entity.setOldPosAndRot();
entity.tickCount++;
entity.totalEntityAge++; // Paper - age-like counter for all entities
@@ -1386,13 +1380,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1390,13 +1384,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
for (Entity entity1 : entity.getPassengers()) {
this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}

View File

@@ -4,18 +4,18 @@ Date: Fri, 23 May 2025 12:01:42 +0900
Subject: [PATCH] Cache block path type
diff --git a/net/minecraft/server/Bootstrap.java b/net/minecraft/server/Bootstrap.java
index 83bc2f6b7774c8753a15dbeb00f0c9103713fd1b..20a720253092b4ec0b648edf06a048f92da201e0 100644
--- a/net/minecraft/server/Bootstrap.java
+++ b/net/minecraft/server/Bootstrap.java
@@ -60,6 +60,7 @@ public class Bootstrap {
io.papermc.paper.world.worldgen.OptionallyFlatBedrockConditionSource.bootstrap(); // Paper - Flat bedrock generator settings
});
// Paper end
+ net.minecraft.world.level.block.Blocks.initPathType(); // Leaf - Cache path type
CreativeModeTabs.validate();
wrapStreams();
bootstrapDuration.set(Duration.between(instant, Instant.now()).toMillis());
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 595284787053a5fb7385e8493953c73a19fe7aee..6dec45b376288638433f0d50e474f9713266d99c 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -360,6 +360,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) mobSpawnExecutor.start(); // Pufferfish
+ net.minecraft.world.level.block.Blocks.initPathType(); // Leaf - Cache path type
org.purpurmc.purpur.task.BossBarTask.startAll(); // Purpur - Implement TPSBar
if (org.purpurmc.purpur.PurpurConfig.beeCountPayload) org.purpurmc.purpur.task.BeehiveTask.instance().register(); // Purpur - Give bee counts in beehives to Purpur clients
diff --git a/net/minecraft/world/level/block/Blocks.java b/net/minecraft/world/level/block/Blocks.java
index 303bd27d44e4acfee49334235a6704724e3fd616..d001d0859c9508eb06f05010ab1cb8069f9b87cf 100644
--- a/net/minecraft/world/level/block/Blocks.java

View File

@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Date: Sat, 17 Feb 2024 17:57:08 -0500
Subject: [PATCH] Rail Optimization: optimized PoweredRailBlock logic
Original project: https://github.com/FxMorin/RailOptimization
Full Rewrite of the powered rail iteration logic
that makes powered/activator rails turning on/off up to 4x faster.
This rewrite brings a massive performance boost while keeping the vanilla order. This is achieved by running all the
powered rail logic from a single rail instead of each block iterating separately. Which was not only very
expensive but also completely unnecessary and with a lot of massive overhead
diff --git a/net/minecraft/world/level/block/PoweredRailBlock.java b/net/minecraft/world/level/block/PoweredRailBlock.java
index a2202d2b4352be07b2445064339c61ba6a2521c7..abf0ffa3540963f591f428876c0c671517594c89 100644
--- a/net/minecraft/world/level/block/PoweredRailBlock.java
+++ b/net/minecraft/world/level/block/PoweredRailBlock.java
@@ -122,6 +122,12 @@ public class PoweredRailBlock extends BaseRailBlock {
@Override
protected void updateState(BlockState state, Level level, BlockPos pos, Block block) {
+ // Leaf start - Rail Optimization
+ if (org.dreeam.leaf.config.modules.opt.OptimizedPoweredRails.enabled) {
+ org.dreeam.leaf.world.block.OptimizedPoweredRails.updateState(this, state, level, pos);
+ return;
+ }
+ // Leaf end - Rail Optimization
boolean poweredValue = state.getValue(POWERED);
boolean flag = level.hasNeighborSignal(pos)
|| this.findPoweredRailSignal(level, pos, state, true, 0)

View File

@@ -71,8 +71,21 @@ index 2a63e8e725fa97da5d4b9fba6bfe19377c7cfbe1..0c286eabd8e4d5e599d93a151874af6b
set.add(connection.getPlayer().getBukkitEntity().getPlayer());
}
return set;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index cab15624afaeaca5d69206a0b7fff5da54e5ef29..4533fbbbc35ed92177f7704f243a1614a1773ca1 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2892,7 +2892,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
Iterator<AttributeInstance> iterator = collection.iterator();
while (iterator.hasNext()) {
AttributeInstance genericInstance = iterator.next();
- if (genericInstance.getAttribute() == Attributes.MAX_HEALTH) {
+ if (genericInstance != null && genericInstance.getAttribute() == Attributes.MAX_HEALTH) { // Leaf - Multithreaded tracker
iterator.remove();
break;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 3a3fbbfa0b82092cd9ac8eab2d179fb9f590aec8..566eab1add471266c8617747c8c25ee04e13c02f 100644
index 5300a513a295d472752d31a6e8af48bb64b06704..fec0f3b72c1808c5019e95760e3fef19c45e1be0 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1751,6 +1751,26 @@ public class CraftEventFactory {

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Date: Thu, 24 Apr 2025 16:36:16 -0400
Subject: [PATCH] Paw optimization
Some random optimizations
diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
index 34a2e07dabc546799df6881de30252397d856f05..d8aca1d7ffad3eccaae676263a41a9a1f04988b9 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
@@ -244,13 +244,37 @@ public class CraftBlockData implements BlockData {
if (!states.isEmpty()) {
stateString.append('[');
- stateString.append(states.entrySet().stream().map(StateHolder.PROPERTY_ENTRY_TO_STRING_FUNCTION).collect(Collectors.joining(",")));
+ // Leaf start - paw optimization
+ int i = 0;
+ for (Map.Entry<Property<?>, Comparable<?>> propertyEntry : states.entrySet()) {
+ if (propertyEntry == null) {
+ stateString.append("<NULL>");
+ } else {
+ Property<?> property = propertyEntry.getKey();
+ Comparable<?> value = propertyEntry.getValue();
+
+ stateString.append(property.getName()).append("=").append(getValueName(property, value));
+ }
+
+ if (i < states.size() - 1) {
+ stateString.append(",");
+ }
+
+ i++;
+ }
+ // Leaf end - paw optimization
stateString.append(']');
}
return stateString.toString();
}
+ // Leaf start - paw optimization
+ private <T extends Comparable<T>> String getValueName(Property<T> property, Comparable<?> value) {
+ return property.getName((T) value);
+ }
+ // Leaf end - paw optimization
+
public Map<String, String> toStates(boolean hideUnspecified) {
return (hideUnspecified && this.parsedStates != null) ? CraftBlockData.toStates(this.parsedStates) : CraftBlockData.toStates(this.state.getValues());
}

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sun, 1 Jun 2025 18:13:24 +0200
Subject: [PATCH] optimise ReferenceList
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java
index 8df9406b77eb3c225ebf88bf76a7adb666452f3b..14ac2a533e0b882f26ee4a11f8d6bccfe752c750 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java
@@ -47,17 +47,21 @@ public final class ReferenceList<E> implements Iterable<E> {
// move the object at the end to this index
final int endIndex = --this.count;
- final E end = (E)this.references[endIndex];
if (index != endIndex) {
+ // The removed element was not the last one.
+ // Move the element that was at 'endIndex' (the old tail) to 'index'.
+ final E end = (E)this.references[endIndex];
// not empty after this call
this.referenceToIndex.put(end, index); // update index
+ this.references[index] = end;
}
- this.references[index] = end;
+ // Null out the slot at 'endIndex'.
+ // If 'index == endIndex', this was the slot of the removed element.
+ // If 'index != endIndex', this was the original slot of the moved element 'end'.
this.references[endIndex] = null;
return true;
}
-
public boolean add(final E obj) {
final int count = this.count;
final int currIndex = this.referenceToIndex.putIfAbsent(obj, count);

View File

@@ -18,7 +18,6 @@ public class AsyncGoalExecutor {
protected final SpscIntQueue queue;
protected final SpscIntQueue wake;
protected final IntArrayList submit;
private final AsyncGoalThread thread;
private final ServerLevel world;
private long midTickCount = 0L;
@@ -27,7 +26,6 @@ public class AsyncGoalExecutor {
this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.submit = new IntArrayList();
this.thread = thread;
}
boolean wake(int id) {
@@ -46,7 +44,18 @@ public class AsyncGoalExecutor {
public final void tick() {
batchSubmit();
LockSupport.unpark(thread);
while (true) {
OptionalInt result = this.wake.recv();
if (result.isEmpty()) {
break;
}
int id = result.getAsInt();
if (poll(id) && !this.queue.send(id)) {
do {
wake(id);
} while (poll(id));
}
}
}
private void batchSubmit() {
@@ -67,18 +76,6 @@ public class AsyncGoalExecutor {
}
public final void midTick() {
while (true) {
OptionalInt result = this.wake.recv();
if (result.isEmpty()) {
break;
}
int id = result.getAsInt();
if (poll(id) && !this.queue.send(id)) {
do {
wake(id);
} while (poll(id));
}
}
if (AsyncTargetFinding.threshold <= 0L || (midTickCount % AsyncTargetFinding.threshold) == 0L) {
batchSubmit();
}

View File

@@ -1,5 +1,6 @@
package org.dreeam.leaf.async.chunk;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.minecraft.Util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -14,13 +15,13 @@ public class AsyncChunkSend {
public static final ExecutorService POOL = new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
new com.google.common.util.concurrent.ThreadFactoryBuilder()
.setPriority(Thread.NORM_PRIORITY - 2)
new ThreadFactoryBuilder()
.setPriority(Thread.NORM_PRIORITY)
.setNameFormat("Leaf Async Chunk Send Thread")
.setUncaughtExceptionHandler(Util::onThreadException)
.setThreadFactory(AsyncChunkSendThread::new)
.build(),
new ThreadPoolExecutor.DiscardPolicy()
new ThreadPoolExecutor.CallerRunsPolicy()
);
public static final Logger LOGGER = LogManager.getLogger("Leaf Async Chunk Send");
}

View File

@@ -5,6 +5,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.pathfinder.Path;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dreeam.leaf.config.modules.async.AsyncPathfinding;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,8 +30,8 @@ public class AsyncPathProcessor {
private static long lastWarnMillis = System.currentTimeMillis();
private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
1,
org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingMaxThreads,
org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
AsyncPathfinding.asyncPathfindingMaxThreads,
AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
getQueueImpl(),
new ThreadFactoryBuilder()
.setNameFormat(THREAD_PREFIX + " Thread - %d")
@@ -44,7 +45,7 @@ public class AsyncPathProcessor {
public void rejectedExecution(Runnable rejectedTask, ThreadPoolExecutor executor) {
BlockingQueue<Runnable> workQueue = executor.getQueue();
if (!executor.isShutdown()) {
switch (org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingRejectPolicy) {
switch (AsyncPathfinding.asyncPathfindingRejectPolicy) {
case FLUSH_ALL -> {
if (!workQueue.isEmpty()) {
List<Runnable> pendingTasks = new ArrayList<>(workQueue.size());
@@ -98,7 +99,7 @@ public class AsyncPathProcessor {
}
private static BlockingQueue<Runnable> getQueueImpl() {
final int queueCapacity = org.dreeam.leaf.config.modules.async.AsyncPathfinding.asyncPathfindingQueueSize;
final int queueCapacity = AsyncPathfinding.asyncPathfindingQueueSize;
return new LinkedBlockingQueue<>(queueCapacity);
}

View File

@@ -32,15 +32,6 @@ public class MultithreadedTracker {
private static long lastWarnMillis = System.currentTimeMillis();
private static ThreadPoolExecutor TRACKER_EXECUTOR = null;
private record SendChanges(Object[] entities, int size) implements Runnable {
@Override
public void run() {
for (int i = 0; i < size; i++) {
((ServerEntity) entities[i]).sendDirtyEntityData();
}
}
}
private MultithreadedTracker() {
}
@@ -80,7 +71,6 @@ public class MultithreadedTracker {
// Move tracking to off-main
TRACKER_EXECUTOR.execute(() -> {
ReferenceArrayList<ServerEntity> sendDirty = new ReferenceArrayList<>();
for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue;
@@ -88,19 +78,12 @@ public class MultithreadedTracker {
if (tracker == null) continue;
// Don't Parallel Tick Tracker of Entity
synchronized (tracker.sync) {
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
synchronized (tracker) {
var trackedChunk = nearbyPlayers.getChunk(entity.chunkPosition());
tracker.moonrise$tick(trackedChunk);
tracker.serverEntity.sendChanges();
if (tracker.serverEntity.wantSendDirtyEntityData) {
tracker.serverEntity.wantSendDirtyEntityData = false;
sendDirty.add(tracker.serverEntity);
}
}
}
if (!sendDirty.isEmpty()) {
level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size()));
}
});
}
@@ -121,7 +104,7 @@ public class MultithreadedTracker {
if (tracker == null) continue;
synchronized (tracker.sync) {
synchronized (tracker) {
tickTask[index] = tracker.leafTickCompact(nearbyPlayers.getChunk(entity.chunkPosition()));
sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
}
@@ -140,22 +123,6 @@ public class MultithreadedTracker {
sendChanges.run();
}
ReferenceArrayList<ServerEntity> sendDirty = new ReferenceArrayList<>();
for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue;
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
if (tracker == null) continue;
if (tracker.serverEntity.wantSendDirtyEntityData) {
tracker.serverEntity.wantSendDirtyEntityData = false;
sendDirty.add(tracker.serverEntity);
}
}
if (!sendDirty.isEmpty()) {
level.getServer().execute(new SendChanges(sendDirty.elements(), sendDirty.size()));
}
});
}

View File

@@ -227,8 +227,12 @@ public class LeafConfig {
extraConfigs.addAll(Arrays.asList(existing.split(",")));
}
// Use same way in spark's BukkitServerConfigProvider#getNestedFiles to get all world configs
// It may spam in the spark profiler, but it's ok, since spark uses YamlConfigParser.INSTANCE to
// get configs defined in extra config flag instead of using SplitYamlConfigParser.INSTANCE
for (World world : Bukkit.getWorlds()) {
extraConfigs.add(world.getWorldFolder().getName() + "/gale-world.yml"); // Gale world config
Path galeWorldFolder = world.getWorldFolder().toPath().resolve("gale-world.yml");
extraConfigs.add(galeWorldFolder.toString().replace("\\", "/").replace("./", "")); // Gale world config
}
return extraConfigs;

View File

@@ -8,15 +8,18 @@ import org.dreeam.leaf.config.annotations.Experimental;
public class SparklyPaperParallelWorldTicking extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".parallel-world-tracking";
} // TODO: Correct config key when stable
return EnumConfigCategory.ASYNC.getBaseKeyName() + ".parallel-world-ticking";
}
@Experimental
public static boolean enabled = false;
public static int threads = 8;
public static boolean logContainerCreationStacktraces = false;
public static boolean disableHardThrow = false;
@Deprecated
public static boolean runAsyncTasksSync = false;
// STRICT, BUFFERED, DISABLED
public static String asyncUnsafeReadHandling = "BUFFERED";
@Override
public void onLoaded() {
@@ -34,15 +37,30 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules {
} else {
threads = 0;
}
logContainerCreationStacktraces = config.getBoolean(getBasePath() + ".log-container-creation-stacktraces", logContainerCreationStacktraces);
logContainerCreationStacktraces = enabled && logContainerCreationStacktraces;
disableHardThrow = config.getBoolean(getBasePath() + ".disable-hard-throw", disableHardThrow);
disableHardThrow = enabled && disableHardThrow;
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", runAsyncTasksSync);
runAsyncTasksSync = enabled && runAsyncTasksSync;
asyncUnsafeReadHandling = config.getString(getBasePath() + ".async-unsafe-read-handling", asyncUnsafeReadHandling).toUpperCase();
if (!asyncUnsafeReadHandling.equals("STRICT") && !asyncUnsafeReadHandling.equals("BUFFERED") && !asyncUnsafeReadHandling.equals("DISABLED")) {
LeafConfig.LOGGER.warn("Invalid value for {}.async-unsafe-read-handling: {}, fallback to STRICT.", getBasePath(), asyncUnsafeReadHandling);
asyncUnsafeReadHandling = "STRICT";
}
if (!enabled) {
asyncUnsafeReadHandling = "DISABLED";
}
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", false); // Default to false now
if (runAsyncTasksSync) {
LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated. Use 'async-unsafe-read-handling: STRICT' for similar safety checks or 'BUFFERED' for buffered reads.", getBasePath());
}
if (enabled) {
LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads);
}
runAsyncTasksSync = enabled && runAsyncTasksSync; // Auto-disable if main feature is off
}
}

View File

@@ -0,0 +1,18 @@
package org.dreeam.leaf.config.modules.opt;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
public class OptimizedPoweredRails extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.PERF.getBaseKeyName() + ".optimized-powered-rails";
}
public static boolean enabled = false;
@Override
public void onLoaded() {
enabled = config().getBoolean(getBasePath(), enabled);
}
}

View File

@@ -11,7 +11,7 @@ import java.util.*;
import java.util.AbstractMap.SimpleEntry;
// fast array backend map with O(1) get & put & remove
public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, AttributeInstance>, Cloneable {
public final class AttributeInstanceArrayMap implements Map<Holder<Attribute>, AttributeInstance>, Cloneable {
private int size = 0;
private transient AttributeInstance[] a = new AttributeInstance[32];
@@ -46,17 +46,17 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final int size() {
public int size() {
return size;
}
@Override
public final boolean isEmpty() {
public boolean isEmpty() {
return size == 0;
}
@Override
public final boolean containsKey(Object key) {
public boolean containsKey(Object key) {
if (key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute) {
int uid = attribute.uid;
return uid >= 0 && uid < a.length && a[uid] != null;
@@ -65,22 +65,22 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final boolean containsValue(Object value) {
for (final AttributeInstance instance : a) {
if (Objects.equals(value, instance)) {
return true;
}
}
return false;
public boolean containsValue(Object value) {
return value instanceof AttributeInstance val && Objects.equals(getInstance(val.getAttribute().value().uid), val);
}
@Override
public final AttributeInstance get(Object key) {
public AttributeInstance get(Object key) {
return key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null;
}
@Nullable
public AttributeInstance getInstance(int key) {
return a[key];
}
@Override
public final AttributeInstance put(@NotNull Holder<Attribute> key, AttributeInstance value) {
public AttributeInstance put(@NotNull Holder<Attribute> key, AttributeInstance value) {
int uid = key.value().uid;
AttributeInstance prev = a[uid];
setByIndex(uid, value);
@@ -88,7 +88,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final AttributeInstance remove(Object key) {
public AttributeInstance remove(Object key) {
if (!(key instanceof Holder<?> holder) || !(holder.value() instanceof Attribute attribute)) return null;
int uid = attribute.uid;
AttributeInstance prev = a[uid];
@@ -97,7 +97,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final void putAll(@NotNull Map<? extends Holder<Attribute>, ? extends AttributeInstance> m) {
public void putAll(@NotNull Map<? extends Holder<Attribute>, ? extends AttributeInstance> m) {
for (AttributeInstance e : m.values()) {
if (e != null) {
setByIndex(e.getAttribute().value().uid, e);
@@ -106,13 +106,13 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final void clear() {
public void clear() {
Arrays.fill(a, null);
size = 0;
}
@Override
public final @NotNull Set<Holder<Attribute>> keySet() {
public @NotNull Set<Holder<Attribute>> keySet() {
if (keys == null) {
keys = new KeySet();
}
@@ -120,7 +120,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final @NotNull Collection<AttributeInstance> values() {
public @NotNull Collection<AttributeInstance> values() {
if (values == null) {
values = new Values();
}
@@ -128,7 +128,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final @NotNull Set<Entry<Holder<Attribute>, AttributeInstance>> entrySet() {
public @NotNull Set<Entry<Holder<Attribute>, AttributeInstance>> entrySet() {
if (entries == null) {
entries = new EntrySet();
}
@@ -136,13 +136,23 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof AttributeInstanceArrayMap that)) return false;
return size == that.size && Arrays.equals(a, that.a);
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Map<?, ?> s)) return false;
if (s.size() != size()) return false;
if (o instanceof AttributeInstanceArrayMap that) {
return Arrays.equals(a, that.a);
}
for (Entry<?, ?> e : s.entrySet()) {
if (!Objects.equals(get(e.getKey()), e.getValue())) {
return false;
}
}
return true;
}
@Override
public final int hashCode() {
public int hashCode() {
return Arrays.hashCode(a);
}
@@ -192,7 +202,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
if (!hasNext()) throw new NoSuchElementException();
currentIndex = nextIndex;
nextIndex = findNextOccupied(nextIndex + 1);
return BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(currentIndex);
return BuiltInRegistries.ATTRIBUTE.get(currentIndex).orElseThrow();
}
@Override
@@ -279,7 +289,7 @@ public class AttributeInstanceArrayMap implements Map<Holder<Attribute>, Attribu
public Entry<Holder<Attribute>, AttributeInstance> next() {
if (!hasNext()) throw new NoSuchElementException();
currentIndex = nextIndex;
Holder<Attribute> key = BuiltInRegistries.ATTRIBUTE.asHolderIdMap().byIdOrThrow(nextIndex);
Holder<Attribute> key = BuiltInRegistries.ATTRIBUTE.get(nextIndex).orElseThrow();
AttributeInstance value = a[nextIndex];
nextIndex = findNextOccupied(nextIndex + 1);
return new SimpleEntry<>(key, value) {

View File

@@ -0,0 +1,113 @@
package org.dreeam.leaf.util.map;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Array;
import java.util.*;
public final class AttributeInstanceSet extends AbstractCollection<AttributeInstance> implements Set<AttributeInstance> {
public final IntSet inner;
public final AttributeInstanceArrayMap map;
public AttributeInstanceSet(AttributeInstanceArrayMap map) {
this.map = map;
inner = new IntArraySet();
}
@Override
public boolean add(AttributeInstance instance) {
return inner.add(instance.getAttribute().value().uid);
}
@Override
public boolean remove(Object o) {
return o instanceof AttributeInstance instance && inner.remove(instance.getAttribute().value().uid);
}
@Override
public @NotNull Iterator<AttributeInstance> iterator() {
return new CloneIterator(inner.toIntArray(), map);
}
@Override
public int size() {
return inner.size();
}
@Override
public boolean isEmpty() {
return inner.isEmpty();
}
@Override
public void clear() {
inner.clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof AttributeInstance instance) {
return inner.contains(instance.getAttribute().value().uid);
}
return false;
}
@Override
public AttributeInstance @NotNull [] toArray() {
int[] innerClone = inner.toIntArray();
AttributeInstance[] arr = new AttributeInstance[innerClone.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = map.getInstance(innerClone[i]);
}
return arr;
}
@SuppressWarnings({"unchecked"})
@Override
public <T> T @NotNull [] toArray(T[] a) {
if (a == null || (a.getClass() == AttributeInstance[].class && a.length == 0)) {
return (T[]) toArray();
}
if (a.length < size()) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size());
}
System.arraycopy((T[]) toArray(), 0, a, 0, size());
if (a.length > size()) {
a[size()] = null;
}
return a;
}
static class CloneIterator implements Iterator<AttributeInstance> {
private final int[] array;
private int index = 0;
private final AttributeInstanceArrayMap map;
CloneIterator(int[] array, AttributeInstanceArrayMap map) {
this.array = array;
this.map = map;
}
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public AttributeInstance next() {
if (!hasNext()) throw new NoSuchElementException();
return map.getInstance(array[index++]);
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Set<?> s)) return false;
if (s.size() != size()) return false;
return containsAll(s);
}
}

View File

@@ -1,4 +1,4 @@
package org.dreeam.leaf.util.biome;
package org.dreeam.leaf.world.biome;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;

View File

@@ -0,0 +1,333 @@
package org.dreeam.leaf.world.block;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.PoweredRailBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RailShape;
import java.util.HashMap;
import static net.minecraft.world.level.block.Block.*;
import static net.minecraft.world.level.block.PoweredRailBlock.POWERED;
import static net.minecraft.world.level.block.PoweredRailBlock.SHAPE;
public class OptimizedPoweredRails {
private static final Direction[] EAST_WEST_DIR = new Direction[]{Direction.WEST, Direction.EAST};
private static final Direction[] NORTH_SOUTH_DIR = new Direction[]{Direction.SOUTH, Direction.NORTH};
private static final int UPDATE_FORCE_PLACE = UPDATE_MOVE_BY_PISTON | UPDATE_KNOWN_SHAPE | UPDATE_CLIENTS;
private static int RAIL_POWER_LIMIT = 8;
private static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) {
BlockState oldState = level.getBlockState(pos);
Block.updateOrDestroy(
oldState,
oldState.updateShape(level, level, pos, direction.getOpposite(), fromPos, state, level.random),
level,
pos,
UPDATE_CLIENTS & -34,
0
);
}
public static int getRailPowerLimit() {
return RAIL_POWER_LIMIT;
}
public static void setRailPowerLimit(int powerLimit) {
RAIL_POWER_LIMIT = powerLimit;
}
public static void updateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) {
boolean shouldBePowered = level.hasNeighborSignal(pos) ||
self.findPoweredRailSignal(level, pos, state, true, 0) ||
self.findPoweredRailSignal(level, pos, state, false, 0);
if (shouldBePowered != state.getValue(POWERED)) {
RailShape railShape = state.getValue(SHAPE);
if (railShape.isSlope()) {
level.setBlock(pos, state.setValue(POWERED, shouldBePowered), 3);
level.updateNeighborsAtExceptFromFacing(pos.below(), self, Direction.UP, null);
level.updateNeighborsAtExceptFromFacing(pos.above(), self, Direction.DOWN, null); // isSlope
} else if (shouldBePowered) {
powerLane(self, level, pos, state, railShape);
} else {
dePowerLane(self, level, pos, state, railShape);
}
}
}
private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level world, BlockPos pos,
boolean bl, int distance, RailShape shape,
HashMap<BlockPos, Boolean> checkedPos) {
BlockState blockState = world.getBlockState(pos);
boolean speedCheck = checkedPos.containsKey(pos) && checkedPos.get(pos);
if (speedCheck) {
return world.hasNeighborSignal(pos) ||
findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos);
} else {
if (blockState.is(self)) {
RailShape railShape = blockState.getValue(SHAPE);
if (shape == RailShape.EAST_WEST && (
railShape == RailShape.NORTH_SOUTH ||
railShape == RailShape.ASCENDING_NORTH ||
railShape == RailShape.ASCENDING_SOUTH
) || shape == RailShape.NORTH_SOUTH && (
railShape == RailShape.EAST_WEST ||
railShape == RailShape.ASCENDING_EAST ||
railShape == RailShape.ASCENDING_WEST
)) {
return false;
} else if (blockState.getValue(POWERED)) {
return world.hasNeighborSignal(pos) ||
findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos);
} else {
return false;
}
}
return false;
}
}
private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level,
BlockPos pos, BlockState state, boolean bl, int distance,
HashMap<BlockPos, Boolean> checkedPos) {
if (distance >= RAIL_POWER_LIMIT - 1) return false;
int i = pos.getX();
int j = pos.getY();
int k = pos.getZ();
boolean bl2 = true;
RailShape railShape = state.getValue(SHAPE);
switch (railShape.ordinal()) {
case 0 -> {
if (bl) ++k;
else --k;
}
case 1 -> {
if (bl) --i;
else ++i;
}
case 2 -> {
if (bl) {
--i;
} else {
++i;
++j;
bl2 = false;
}
railShape = RailShape.EAST_WEST;
}
case 3 -> {
if (bl) {
--i;
++j;
bl2 = false;
} else {
++i;
}
railShape = RailShape.EAST_WEST;
}
case 4 -> {
if (bl) {
++k;
} else {
--k;
++j;
bl2 = false;
}
railShape = RailShape.NORTH_SOUTH;
}
case 5 -> {
if (bl) {
++k;
++j;
bl2 = false;
} else {
--k;
}
railShape = RailShape.NORTH_SOUTH;
}
}
return findPoweredRailSignalFaster(
self, level, new BlockPos(i, j, k),
bl, distance, railShape, checkedPos
) ||
(bl2 && findPoweredRailSignalFaster(
self, level, new BlockPos(i, j - 1, k),
bl, distance, railShape, checkedPos
));
}
private static void powerLane(PoweredRailBlock self, Level world, BlockPos pos,
BlockState mainState, RailShape railShape) {
world.setBlock(pos, mainState.setValue(POWERED, true), UPDATE_FORCE_PLACE);
HashMap<BlockPos, Boolean> checkedPos = new HashMap<>();
checkedPos.put(pos, true);
int[] count = new int[2];
if (railShape == RailShape.NORTH_SOUTH) { // Order: +z, -z
for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
setRailPositionsPower(self, world, pos, checkedPos, count, i, NORTH_SOUTH_DIR[i]);
}
updateRails(self, false, world, pos, mainState, count);
} else if (railShape == RailShape.EAST_WEST) { // Order: -x, +x
for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
setRailPositionsPower(self, world, pos, checkedPos, count, i, EAST_WEST_DIR[i]);
}
updateRails(self, true, world, pos, mainState, count);
}
}
private static void dePowerLane(PoweredRailBlock self, Level world, BlockPos pos,
BlockState mainState, RailShape railShape) {
world.setBlock(pos, mainState.setValue(POWERED, false), UPDATE_FORCE_PLACE);
int[] count = new int[2];
if (railShape == RailShape.NORTH_SOUTH) { // Order: +z, -z
for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
setRailPositionsDePower(self, world, pos, count, i, NORTH_SOUTH_DIR[i]);
}
updateRails(self, false, world, pos, mainState, count);
} else if (railShape == RailShape.EAST_WEST) { // Order: -x, +x
for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
setRailPositionsDePower(self, world, pos, count, i, EAST_WEST_DIR[i]);
}
updateRails(self, true, world, pos, mainState, count);
}
}
private static void setRailPositionsPower(PoweredRailBlock self, Level world, BlockPos pos,
HashMap<BlockPos, Boolean> checkedPos, int[] count, int i, Direction dir) {
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
BlockPos newPos = pos.relative(dir, z);
BlockState state = world.getBlockState(newPos);
if (checkedPos.containsKey(newPos)) {
if (!checkedPos.get(newPos)) break;
count[i]++;
} else if (!state.is(self) || state.getValue(POWERED) || !(
world.hasNeighborSignal(newPos) ||
findPoweredRailSignalFaster(self, world, newPos, state, true, 0, checkedPos) ||
findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos)
)) {
checkedPos.put(newPos, false);
break;
} else {
checkedPos.put(newPos, true);
world.setBlock(newPos, state.setValue(POWERED, true), UPDATE_FORCE_PLACE);
count[i]++;
}
}
}
private static void setRailPositionsDePower(PoweredRailBlock self, Level world, BlockPos pos,
int[] count, int i, Direction dir) {
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
BlockPos newPos = pos.relative(dir, z);
BlockState state = world.getBlockState(newPos);
if (!state.is(self) || !state.getValue(POWERED) || world.hasNeighborSignal(newPos) ||
self.findPoweredRailSignal(world, newPos, state, true, 0) ||
self.findPoweredRailSignal(world, newPos, state, false, 0)) break;
world.setBlock(newPos, state.setValue(POWERED, false), UPDATE_FORCE_PLACE);
count[i]++;
}
}
private static void shapeUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, BlockState mainState,
int endPos, Direction direction, int currentPos, BlockPos blockPos) {
if (currentPos == endPos) {
BlockPos newPos = pos.relative(direction, currentPos + 1);
giveShapeUpdate(world, mainState, newPos, pos, direction);
BlockState state = world.getBlockState(blockPos);
if (state.is(self) && state.getValue(SHAPE).isSlope()) giveShapeUpdate(world, mainState, newPos.above(), pos, direction);
}
}
private static void neighborUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, int endPos,
Direction direction, Block block, int currentPos, BlockPos blockPos) {
if (currentPos == endPos) {
BlockPos newPos = pos.relative(direction, currentPos + 1);
world.neighborChanged(newPos, block, null);
BlockState state = world.getBlockState(blockPos);
if (state.is(self) && state.getValue(SHAPE).isSlope()) world.neighborChanged(newPos.above(), block, null);
}
}
private static void updateRailsSectionEastWestShape(PoweredRailBlock self, Level world, BlockPos pos,
int c, BlockState mainState, Direction dir,
int[] count, int countAmt) {
BlockPos pos1 = pos.relative(dir, c);
if (c == 0 && count[1] == 0) giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite());
shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1);
giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN);
giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP);
giveShapeUpdate(world, mainState, pos1.north(), pos, Direction.NORTH);
giveShapeUpdate(world, mainState, pos1.south(), pos, Direction.SOUTH);
}
private static void updateRailsSectionNorthSouthShape(PoweredRailBlock self, Level world, BlockPos pos,
int c, BlockState mainState, Direction dir,
int[] count, int countAmt) {
BlockPos pos1 = pos.relative(dir, c);
giveShapeUpdate(world, mainState, pos1.west(), pos, Direction.WEST);
giveShapeUpdate(world, mainState, pos1.east(), pos, Direction.EAST);
giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN);
giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP);
shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1);
if (c == 0 && count[1] == 0) giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite());
}
private static void updateRails(PoweredRailBlock self, boolean eastWest, Level world,
BlockPos pos, BlockState mainState, int[] count) {
if (eastWest) {
for (int i = 0; i < EAST_WEST_DIR.length; ++i) {
int countAmt = count[i];
if (i == 1 && countAmt == 0) continue;
Direction dir = EAST_WEST_DIR[i];
Block block = mainState.getBlock();
for (int c = countAmt; c >= i; c--) {
BlockPos p = pos.relative(dir, c);
if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, null);
neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p);
world.neighborChanged(p.below(), block, null);
world.neighborChanged(p.above(), block, null);
world.neighborChanged(p.north(), block, null);
world.neighborChanged(p.south(), block, null);
BlockPos pos2 = pos.relative(dir, c).below();
world.neighborChanged(pos2.below(), block, null);
world.neighborChanged(pos2.north(), block, null);
world.neighborChanged(pos2.south(), block, null);
if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, null);
if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()).below(), block, null);
}
for (int c = countAmt; c >= i; c--)
updateRailsSectionEastWestShape(self, world, pos, c, mainState, dir, count, countAmt);
}
} else {
for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) {
int countAmt = count[i];
if (i == 1 && countAmt == 0) continue;
Direction dir = NORTH_SOUTH_DIR[i];
Block block = mainState.getBlock();
for (int c = countAmt; c >= i; c--) {
BlockPos p = pos.relative(dir, c);
world.neighborChanged(p.west(), block, null);
world.neighborChanged(p.east(), block, null);
world.neighborChanged(p.below(), block, null);
world.neighborChanged(p.above(), block, null);
neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p);
if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, null);
BlockPos pos2 = pos.relative(dir, c).below();
world.neighborChanged(pos2.west(), block, null);
world.neighborChanged(pos2.east(), block, null);
world.neighborChanged(pos2.below(), block, null);
if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, null);
if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()).below(), block, null);
}
for (int c = countAmt; c >= i; c--)
updateRailsSectionNorthSouthShape(self, world, pos, c, mainState, dir, count, countAmt);
}
}
}
}

View File

@@ -13,4 +13,4 @@
- [ ] Check Purpur's Projectile offset config, in BowItem shoot
- [ ] Remove Gale's attribute patch
- [ ] Check SparklyPaper's mapitem update skip
- [ ] Update from Leaf 1.21.4 (curr commit: `a022d84c5b5b52f7e8a62f6bff774d0c23176ed5`)
- [ ] Update from Leaf 1.21.4 (curr commit: `12711630d4ca8788366f8abd47275c4c262ff13f`)