9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0217-SparklyPaper-Parallel-world-ticking.patch
2025-06-27 19:11:50 +08:00

1490 lines
102 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Altiami <yoshimo.kristin@gmail.com>
Date: Wed, 5 Mar 2025 13:13:24 -0800
Subject: [PATCH] SparklyPaper: Parallel world ticking
Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
index ea4010df54dbd17cdae22d671ea1e4bd7b685b3e..8d53cb917e9f623a67aba066c6a21f278f1f0967 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
@@ -1116,7 +1116,7 @@ public final class ChunkHolderManager {
if (changedFullStatus.isEmpty()) {
return;
}
- if (!TickThread.isTickThread()) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !TickThread.isTickThreadFor(world) || !TickThread.isTickThread()) { // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
this.taskScheduler.scheduleChunkTask(() -> {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1142,7 +1142,12 @@ public final class ChunkHolderManager {
// note: never call while inside the chunk system, this will absolutely break everything
public void processUnloads() {
- TickThread.ensureTickThread("Cannot unload chunks off-main");
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ TickThread.ensureTickThread(world, "Cannot unload chunks off-main"); // SparklyPaper - parallel world ticking
+ else
+ TickThread.ensureTickThread("Cannot unload chunks off-main");
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot unload chunks recursively");
@@ -1424,7 +1429,7 @@ public final class ChunkHolderManager {
List<NewChunkHolder> changedFullStatus = null;
- final boolean isTickThread = TickThread.isTickThread();
+ final boolean isTickThread = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && TickThread.isTickThreadFor(world) || TickThread.isTickThread(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
boolean ret = false;
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..b288d57d9f7bd0ccf1877cf9920bd67288ff22f7 100644
--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
@@ -829,14 +829,10 @@ public final class RedstoneWireTurbo {
j = getMaxCurrentStrength(upd, j);
int l = 0;
- wire.shouldSignal = false;
- // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
- // and I'm not ready to try to replicate even more functionality from
- // elsewhere in Minecraft into this accelerator. So sadly, we must
- // suffer the performance hit of this very expensive call. If there
- // is consistency to what this call returns, we may be able to cache it.
- final int k = worldIn.getBestNeighborSignal(upd.self);
- wire.shouldSignal = true;
+ // Leaf start - SparklyPaper - parallel world ticking
+ // This now correctly calls the (conditionally) thread-safe method in RedStoneWireBlock
+ final int k = wire.getBlockSignal(worldIn, upd.self);
+ // Leaf end - SparklyPaper - parallel world ticking
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
// If 'k' has the highest level of all neighbors, then the power level of this
diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 582e012222123e5001c34153f2ee1ab1d08935fd..c0bce2293d07ca58cc5bc9e036ab8dcac06c1566 100644
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -401,8 +401,8 @@ public interface DispenseItemBehavior {
// CraftBukkit start
level.captureTreeGeneration = false;
if (!level.capturedBlockStates.isEmpty()) {
- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType;
- net.minecraft.world.level.block.SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.getTreeTypeRT(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(null); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level.getWorld());
List<org.bukkit.block.BlockState> states = new java.util.ArrayList<>(level.capturedBlockStates.values());
level.capturedBlockStates.clear();
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 4bf840705bec251aa7e5595fa16bf8e1a9a7d2e9..543bdb926426bfd5be01d0c23e8c9e274b94485c 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -290,6 +290,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public boolean lagging = false; // Purpur - Lagging threshold
protected boolean upnp = false; // Purpur - UPnP Port Forwarding
public final Set<net.minecraft.world.entity.Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
+ public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // SparklyPaper - parallel world ticking
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -321,24 +322,36 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private long lastMidTickExecute;
private long lastMidTickExecuteFailure;
+ // Leaf start - SparklyPaper - parallel world ticking mod
+ private boolean tickLevelMidTickTasks(ServerLevel world) {
+ long currTime = System.nanoTime();
+ if (currTime - world.moonrise$getLastMidTickFailure() <= TASK_EXECUTION_FAILURE_BACKOFF) {
+ return false;
+ }
+ if (!world.getChunkSource().pollTask()) {
+ // we need to back off if this fails
+ world.moonrise$setLastMidTickFailure(currTime);
+ return false;
+ }
+ return true;
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod
+
private boolean tickMidTickTasks() {
// give all worlds a fair chance at by targeting them all.
// if we execute too many tasks, that's fine - we have logic to correctly handle overuse of allocated time.
- boolean executed = false;
- for (final ServerLevel world : this.getAllLevels()) {
- long currTime = System.nanoTime();
- if (currTime - ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getLastMidTickFailure() <= TASK_EXECUTION_FAILURE_BACKOFF) {
- continue;
- }
- if (!world.getChunkSource().pollTask()) {
- // we need to back off if this fails
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$setLastMidTickFailure(currTime);
- } else {
- executed = true;
+ // Leaf start - SparklyPaper - parallel world ticking mod (only mid-tick the level for the current thread)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && Thread.currentThread() instanceof ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread levelThread) {
+ return this.tickLevelMidTickTasks(levelThread.currentlyTickingServerLevel);
+ } else {
+ boolean executed = false;
+ for (final ServerLevel world : this.getAllLevels()) {
+ executed = executed || this.tickLevelMidTickTasks(world);
}
- }
- return executed;
+ return executed;
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (only mid-tick the level for the current thread)
}
@Override
@@ -915,6 +928,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;
}
@@ -1660,6 +1679,18 @@ 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 {
+ serverLevel.tick(hasTimeLeft);
+ } catch (Throwable levelTickingException) {
+ CrashReport crashReport = CrashReport.forThrowable(levelTickingException, "Exception ticking world");
+ serverLevel.fillReportDetails(crashReport);
+ throw new ReportedException(crashReport);
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (move level ticking logic out for branch convergence)
+
protected void tickChildren(BooleanSupplier hasTimeLeft) {
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
@@ -1726,28 +1757,50 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
- for (ServerLevel serverLevel : this.getAllLevels()) {
- serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
- serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
- serverLevel.updateLagCompensationTick(); // Paper - lag compensation
- net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
- serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables
- /* Drop global time updates
- if (this.tickCount % 20 == 0) {
- this.synchronizeTime(serverLevel);
- }
- // CraftBukkit end */
+ // SparklyPaper start - parallel world ticking
+ java.util.ArrayDeque<java.util.concurrent.Future<ServerLevel>> tasks = new java.util.ArrayDeque<>();
+ try {
+ for (ServerLevel serverLevel : this.getAllLevels()) {
+ serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
+ serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
+ serverLevel.updateLagCompensationTick(); // Paper - lag compensation
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
+ serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables
+ /* Drop global time updates
+ if (this.tickCount % 20 == 0) {
+ this.synchronizeTime(serverLevel);
+ }
+ // CraftBukkit end */
- try {
- serverLevel.tick(hasTimeLeft);
- } catch (Throwable var7) {
- CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
- serverLevel.fillReportDetails(crashReport);
- throw new ReportedException(crashReport);
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ serverLevelTickingSemaphore.acquire();
+ tasks.add(
+ serverLevel.tickExecutor.submit(() -> {
+ ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread currentThread = (ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread) Thread.currentThread();
+ currentThread.currentlyTickingServerLevel = serverLevel;
+
+ try {
+ tickLevel(serverLevel, hasTimeLeft); // Leaf - SparklyPaper - parallel world ticking mod (move level ticking logic out for branch convergence)
+ } finally {
+ serverLevelTickingSemaphore.release();
+ }
+ }, serverLevel)
+ );
+ } else
+ tickLevel(serverLevel, hasTimeLeft);
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
+
+ serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
}
- serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
+ while (!tasks.isEmpty()) {
+ tasks.pop().get();
+ }
+ } catch (java.lang.InterruptedException | java.util.concurrent.ExecutionException e) {
+ throw new RuntimeException(e); // Propagate exception
}
+ // SparklyPaper end - parallel world ticking
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
this.tickConnection();
@@ -1827,6 +1880,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());
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ level.tickExecutor.shutdown(); // SparklyPaper - parallel world ticking (We remove it in here instead of ServerLevel.close() because ServerLevel.close() is never called!)
this.levels = Collections.unmodifiableMap(newLevels);
}
// CraftBukkit end
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index 10ac7393d20a0857be2bfdd856dda448699b3eff..53d081aceaa602cdc77018b434edc6ff3a994f91 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -53,8 +53,11 @@ public class PlayerAdvancements {
private AdvancementTree tree;
private final Map<AdvancementHolder, AdvancementProgress> progress = new LinkedHashMap<>();
private final Set<AdvancementHolder> visible = new HashSet<>();
- private final Set<AdvancementHolder> progressChanged = new HashSet<>();
- private final Set<AdvancementNode> rootsToUpdate = new HashSet<>();
+ // Leaf start - SparklyPaper - parallel world ticking
+ private final Set<AdvancementHolder> progressChanged = new HashSet<>(); // Used when PWT is disabled
+ private final Set<AdvancementHolder> progressChangedConcurrent = java.util.concurrent.ConcurrentHashMap.newKeySet(); // Used when PWT is enabled
+ private final Set<AdvancementNode> rootsToUpdate = new HashSet<>(); // Always managed on player tick thread
+ // Leaf end - SparklyPaper - parallel world ticking
private ServerPlayer player;
@Nullable
private AdvancementHolder lastSelectedTab;
@@ -88,6 +91,7 @@ public class PlayerAdvancements {
this.visible.clear();
this.rootsToUpdate.clear();
this.progressChanged.clear();
+ this.progressChangedConcurrent.clear(); // Leaf - SparklyPaper - parallel world ticking fix - Also clear concurrent set on reload
this.isFirstPacket = true;
this.lastSelectedTab = null;
this.tree = manager.tree();
@@ -151,7 +155,7 @@ public class PlayerAdvancements {
if (org.galemc.gale.configuration.GaleGlobalConfiguration.get().logToConsole.ignoredAdvancements) LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); // Gale - Purpur - do not log ignored advancements
} else {
this.startProgress(advancementHolder, progress);
- this.progressChanged.add(advancementHolder);
+ this.progressChanged.add(advancementHolder); // Leaf - SparklyPaper - parallel world ticking fix - Always add to non-concurrent set during load, flushDirty will handle sync
this.markForVisibilityUpdate(advancementHolder);
}
});
@@ -183,10 +187,12 @@ public class PlayerAdvancements {
return false;
}
// Paper end - Add PlayerAdvancementCriterionGrantEvent
- this.unregisterListeners(advancement);
- this.progressChanged.add(advancement);
- flag = true;
- if (!isDone && orStartProgress.isDone()) {
+ // Leaf start - SparklyPaper - parallel world ticking
+ this.unregisterListeners(advancement); // Must unregister criteria listeners
+ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement);
+ flag = true; // Mark progress changed
+ if (!isDone && orStartProgress.isDone()) { // If the advancement was just completed
+ // Leaf end - SparklyPaper - parallel world ticking
// Paper start - Add Adventure message to PlayerAdvancementDoneEvent
final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> {
return java.util.Optional.ofNullable(
@@ -220,12 +226,14 @@ public class PlayerAdvancements {
AdvancementProgress orStartProgress = this.getOrStartProgress(advancement);
boolean isDone = orStartProgress.isDone();
if (orStartProgress.revokeProgress(criterionKey)) {
- this.registerListeners(advancement);
- this.progressChanged.add(advancement);
+ // Leaf start - SparklyPaper - parallel world ticking
+ this.registerListeners(advancement); // Re-register listeners if it's no longer done
+ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancement);
+ // Leaf end - SparklyPaper - parallel world ticking
flag = true;
}
- if (isDone && !orStartProgress.isDone()) {
+ if (isDone && !orStartProgress.isDone()) { // Leaf - SparklyPaper - parallel world ticking - If the advancement was just un-completed
this.markForVisibilityUpdate(advancement);
}
@@ -271,7 +279,10 @@ public class PlayerAdvancements {
}
public void flushDirty(ServerPlayer player, boolean showAdvancements) {
- if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) {
+ // Leaf start - SparklyPaper - parallel world ticking
+ final boolean useConcurrent = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled;
+ final Set<AdvancementHolder> relevantProgressSet = useConcurrent ? this.progressChangedConcurrent : this.progressChanged;
+ if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !relevantProgressSet.isEmpty()) {
Map<ResourceLocation, AdvancementProgress> map = new HashMap<>();
Set<AdvancementHolder> set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically.
Set<ResourceLocation> set1 = new HashSet<>();
@@ -279,16 +290,24 @@ public class PlayerAdvancements {
for (AdvancementNode advancementNode : this.rootsToUpdate) {
this.updateTreeVisibility(advancementNode, set, set1);
}
+ this.rootsToUpdate.clear(); // Roots processed, clear the set
- this.rootsToUpdate.clear();
+ if (!relevantProgressSet.isEmpty()) {
+ Set<AdvancementHolder> toProcess = useConcurrent ? new HashSet<>(relevantProgressSet) : relevantProgressSet;
- for (AdvancementHolder advancementHolder : this.progressChanged) {
- if (this.visible.contains(advancementHolder)) {
+ for (AdvancementHolder advancementHolder : toProcess) {
+ if (this.visible.contains(advancementHolder)) { // Only include progress for visible advancements
map.put(advancementHolder.id(), this.progress.get(advancementHolder));
}
}
- this.progressChanged.clear();
+ if (useConcurrent) {
+ this.progressChangedConcurrent.removeAll(toProcess); // Remove processed items from concurrent set
+ } else {
+ this.progressChanged.clear(); // Clear the regular set
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking
if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) {
player.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map, showAdvancements));
}
@@ -331,10 +350,13 @@ public class PlayerAdvancements {
AdvancementHolder advancementHolder = node.holder();
if (visible) {
if (this.visible.add(advancementHolder)) {
- advancementOutput.add(advancementHolder);
+ // Leaf start - SparklyPaper - parallel world ticking
+ advancementOutput.add(advancementHolder); // Add to visible set for packet
if (this.progress.containsKey(advancementHolder)) {
- this.progressChanged.add(advancementHolder);
+ // If progress exists, mark it changed so the progress data is sent
+ (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.progressChangedConcurrent : this.progressChanged).add(advancementHolder);
}
+ // Leaf end - SparklyPaper - parallel world ticking
}
} else if (this.visible.remove(advancementHolder)) {
idOutput.add(advancementHolder.id());
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 54910c2e1d6e6bb556e536fda060bd09402e04e8..72e871b8c7fee9b5cbd567e03baee80ee4b9c82e 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -198,6 +198,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
org.purpurmc.purpur.PurpurConfig.registerCommands();
// Purpur end - Purpur config files
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.threads); // SparklyPaper - parallel world ticking
+ DedicatedServer.LOGGER.info("Using " + serverLevelTickingSemaphore.availablePermits() + " permits for parallel world ticking"); // SparklyPaper - parallel world ticking
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
// Gale start - Pufferfish - SIMD support
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index ecab2befa1f2f993ea4b4d088529745c2a37b73d..fc86e900e41305287a6cc6d766184c6e28d6189b 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -175,7 +175,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
// call mid-tick tasks for chunk system
if ((i & 7) == 0) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
+ //((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
continue;
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 2ecb73fc7b6754ade93bf16b48c623e6b3a955a9..85bfc91ff163a2a564b7b610e27ff90e053787d0 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -178,7 +178,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final MinecraftServer server;
public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type
private int lastSpawnChunkRadius;
- final EntityTickList entityTickList = new EntityTickList();
+ final EntityTickList entityTickList = new EntityTickList(this); // SparklyPaper - parallel world ticking
// Paper - rewrite chunk system
private final GameEventDispatcher gameEventDispatcher;
public boolean noSave;
@@ -204,6 +204,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;
@@ -693,8 +696,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
@Override
public boolean hasChunk(int chunkX, int chunkZ) {
@@ -727,8 +748,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.toVec3(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.toFluid(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) {
@@ -736,6 +861,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
@@ -1309,9 +1442,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
fluidState.tick(this, pos, blockState);
}
// Paper start - rewrite chunk system
- if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
+ // Leaf start - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
+ ++this.tickedBlocksOrFluids;
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (this.tickedBlocksOrFluids & 7L) != 0L) {
+ this.server.moonrise$executeMidTickTasks();
}
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
// Paper end - rewrite chunk system
}
@@ -1322,9 +1458,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
blockState.tick(this, pos, this.random);
}
// Paper start - rewrite chunk system
- if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
+ // Leaf start - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
+ ++this.tickedBlocksOrFluids;
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (this.tickedBlocksOrFluids & 7L) != 0L) {
+ this.server.moonrise$executeMidTickTasks();
}
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
// Paper end - rewrite chunk system
}
@@ -1575,6 +1714,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
private void addPlayer(ServerPlayer player) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable / async is no longer safe; schedule on main)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
Entity entity = this.getEntity(player.getUUID());
if (entity != null) {
LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
@@ -1587,7 +1728,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// CraftBukkit start
private boolean addEntity(Entity entity, @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
- org.spigotmc.AsyncCatcher.catchOp("entity add"); // 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, "Cannot add entity off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
+ else
+ org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process
// Paper start - extra debug info
if (entity.valid) {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index d507544efafe74ecaffd6a063eff152d349ec76a..bc955da0dff79262dace84d255f27b3271a91ca5 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -456,6 +456,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return this.viewDistanceHolder;
}
// Paper end - rewrite chunk system
+ public boolean hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (fixes bug in DreamResourceReset where the inventory is opened AFTER the player has changed worlds, if you click with the quick tp torch in a chest, because the inventory is opened AFTER the player has teleported)
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile);
@@ -749,6 +750,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@Override
public void tick() {
+ hasTickedAtLeastOnceInNewWorld = true; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
// CraftBukkit start
if (this.joining) {
this.joining = false;
@@ -1398,6 +1400,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
teleportTransition.postTeleportTransition().onTransition(this);
return this;
} else {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel().getWorld().getName() + " to world " + level.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// CraftBukkit start
/*
this.isChangingDimension = true;
@@ -1747,6 +1751,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return OptionalInt.empty();
} else {
// CraftBukkit start
+ // SparklyPaper start - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !hasTickedAtLeastOnceInNewWorld) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ MinecraftServer.LOGGER.warn("Ignoring request to open container " + abstractContainerMenu + " because we haven't ticked in the current world yet!", new Throwable());
+ return OptionalInt.empty();
+ }
+ // SparklyPaper end - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
this.containerMenu = abstractContainerMenu; // Moved up
if (!this.isImmobile())
this.connection
@@ -1811,6 +1821,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
@Override
public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
+ // SparklyPaper start - parallel world ticking (debugging)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.logContainerCreationStacktraces) {
+ MinecraftServer.LOGGER.warn("Closing " + this.getBukkitEntity().getName() + " inventory that was created at", this.containerMenu.containerCreationStacktrace);
+ }
+ // SparklyPaper end - parallel world ticking (debugging)
org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
// Paper end - Inventory close reason
this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 75393b9cdd564e55ba173828c2f7b40498e8ecd7..96202ffdb73cb9d8c63351b4538c64645b91d21c 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -252,6 +252,8 @@ public abstract class PlayerList {
// Leaves end - replay mod api
public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
player.isRealPlayer = true; // Paper
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
GameProfile gameProfile = player.getGameProfile();
@@ -892,6 +894,15 @@ public abstract class PlayerList {
}
public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, @Nullable org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, @Nullable org.bukkit.Location location) {
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !this.server.isSameThread()) {
+ // Respawning is a complex operation that modifies global player lists and can interact with multiple
+ // worlds. It must be executed on the main server thread to ensure thread safety. We block the
+ // calling (world) thread to wait for the result, preserving the synchronous API contract of this method.
+ org.bukkit.Location finalLocation = location;
+ return this.server.submit(() -> this.respawn(player, keepInventory, reason, eventReason, finalLocation)).join();
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
player.stopRiding(); // CraftBukkit
this.players.remove(player);
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
@@ -903,6 +914,7 @@ public abstract class PlayerList {
ServerPlayer serverPlayer = player;
Level fromWorld = player.level();
player.wonGame = false;
+ serverPlayer.hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
// CraftBukkit end
serverPlayer.connection = player.connection;
serverPlayer.restoreFrom(player, keepInventory);
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 69521086fca0c9fbc230817062b65c4269c575fd..82096e6b187e63ad3e848b9757ba2152e8c642b4 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -3321,15 +3321,40 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
if (this.portalProcess != null) {
if (this.portalProcess.processPortalTeleportation(serverLevel, this, this.canUsePortal(false))) {
this.setPortalCooldown();
- TeleportTransition portalDestination = this.portalProcess.getPortalDestination(serverLevel, this);
- if (portalDestination != null) {
- ServerLevel level = portalDestination.newLevel();
- if (this instanceof ServerPlayer // CraftBukkit - always call event for players
- || (level != null && (level.dimension() == serverLevel.dimension() || this.canTeleport(serverLevel, level)))) { // CraftBukkit
- this.teleport(portalDestination);
+ // Leaf start - SparklyPaper - parallel world ticking mod (mark pending teleport to prevent clearing portal process)
+ // TCRF SparklyPaper (Pathothingi) start - parallel world ticking
+ java.util.function.Consumer<Entity> portalEntityTask = entity -> {
+ assert entity.portalProcess != null;
+ // Fix NPE when portalProcess becomes null before task execution
+ // Portal process was likely nulled out (e.g., expired) between scheduling and execution.
+ if (entity.portalProcess == null) {
+ return;
+ }
+
+ if (entity.portalProcess.isParallelCancelledByPlugin()) {
+ entity.portalProcess = null;
+ return;
}
- }
+ TeleportTransition portalDestination = entity.portalProcess.getPortalDestination(serverLevel, entity);
+ if (portalDestination != null) {
+ ServerLevel level = portalDestination.newLevel();
+ if (entity instanceof ServerPlayer // CraftBukkit - always call event for players
+ || (level != null && (level.dimension() == serverLevel.dimension() || entity.canTeleport(serverLevel, level)))) { // CraftBukkit
+ entity.teleport(portalDestination);
+ }
+ }
+ // Add another null check here just in case teleport() somehow nulled it (defensive)
+ if (entity.portalProcess != null) {
+ entity.portalProcess.confirmParallelAsHandled();
+ }
+ };
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ this.portalProcess.setParallelAsScheduled();
+ this.getBukkitEntity().taskScheduler.schedule(portalEntityTask, entity -> {}, 0);
+ } else
+ portalEntityTask.accept(this);
+ // Leaf end - SparklyPaper - parallel world ticking mod (mark pending teleport to prevent clearing portal process)
} else if (this.portalProcess.hasExpired()) {
this.portalProcess = null;
}
@@ -3894,6 +3919,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
private Entity teleportCrossDimension(ServerLevel level, TeleportTransition teleportTransition) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(level, "Cannot teleport entity to another world off-main, from world " + this.level.getWorld().getName() + " to world " + level.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
List<Entity> passengers = this.getPassengers();
List<Entity> list = new ArrayList<>(passengers.size());
this.ejectPassengers();
diff --git a/net/minecraft/world/entity/PortalProcessor.java b/net/minecraft/world/entity/PortalProcessor.java
index 88b07fbb96b20124777889830afa480673629d43..f8bb32840129e57b7799f883cb4570d274520390 100644
--- a/net/minecraft/world/entity/PortalProcessor.java
+++ b/net/minecraft/world/entity/PortalProcessor.java
@@ -11,6 +11,7 @@ public class PortalProcessor {
private BlockPos entryPosition;
private int portalTime;
private boolean insidePortalThisTick;
+ private ParallelPendingTeleportState pendingTeleport = ParallelPendingTeleportState.INACTIVE; // Leaf - SparklyPaper - parallel world ticking mod (prevent clearing portal process)
public PortalProcessor(Portal portal, BlockPos entryPosition) {
this.portal = portal;
@@ -19,6 +20,8 @@ public class PortalProcessor {
}
public boolean processPortalTeleportation(ServerLevel level, Entity entity, boolean canChangeDimensions) {
+ if (this.isParallelTeleportScheduled()) return false; // Leaf - SparklyPaper - parallel world ticking mod (prevent clearing portal process)
+
if (!this.insidePortalThisTick) {
this.decayTick();
return false;
@@ -42,7 +45,7 @@ public class PortalProcessor {
}
public boolean hasExpired() {
- return this.portalTime <= 0;
+ return !this.isParallelTeleportScheduled() && this.portalTime <= 0; // Leaf - SparklyPaper - parallel world ticking mod (prevent clearing portal process)
}
public BlockPos getEntryPosition() {
@@ -68,4 +71,36 @@ public class PortalProcessor {
public boolean isSamePortal(Portal portal) {
return this.portal == portal;
}
+
+ // Leaf start - SparklyPaper - parallel world ticking mod (prevent clearing portal process)
+ public boolean isParallelTeleportPending() {
+ return this.pendingTeleport == ParallelPendingTeleportState.PENDING;
+ }
+
+ public boolean isParallelTeleportScheduled() {
+ return this.pendingTeleport != ParallelPendingTeleportState.INACTIVE;
+ }
+
+ public boolean isParallelCancelledByPlugin() {
+ return this.pendingTeleport == ParallelPendingTeleportState.CANCELLED;
+ }
+
+ public void setParallelAsScheduled() {
+ this.pendingTeleport = ParallelPendingTeleportState.PENDING;
+ }
+
+ public void confirmParallelAsHandled() {
+ this.pendingTeleport = ParallelPendingTeleportState.INACTIVE;
+ }
+
+ public void setParallelAsCancelled() {
+ this.pendingTeleport = ParallelPendingTeleportState.CANCELLED;
+ }
+
+ private enum ParallelPendingTeleportState {
+ INACTIVE,
+ PENDING,
+ CANCELLED
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (prevent clearing portal process)
}
diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
index 3614551856c594f3c0cfee984fcf03fad672b007..f4577f908ca9f279b72d89e5b0822d34b6fb7dd1 100644
--- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
+++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
@@ -44,14 +44,34 @@ public class GoToPotentialJobSite extends Behavior<Villager> {
Optional<GlobalPos> memory = entity.getBrain().getMemory(MemoryModuleType.POTENTIAL_JOB_SITE);
memory.ifPresent(globalPos -> {
BlockPos blockPos = globalPos.pos();
- ServerLevel level1 = level.getServer().getLevel(globalPos.dimension());
- if (level1 != null) {
- PoiManager poiManager = level1.getPoiManager();
- if (poiManager.exists(blockPos, holder -> true)) {
- poiManager.release(blockPos);
- }
+ // Leaf start - SparklyPaper - parallel world ticking
+ ServerLevel entityLevel = level; // Villager's current level
+ ServerLevel poiLevel = entityLevel.getServer().getLevel(globalPos.dimension()); // POI's actual level
+
+ if (poiLevel != null) {
+ Runnable poiOperationsTask = () -> {
+ PoiManager poiManager = poiLevel.getPoiManager();
+ if (poiManager.exists(blockPos, holder -> true)) {
+ poiManager.release(blockPos);
+ }
+ };
+
+ // DebugPackets.sendPoiTicketCountPacket uses the entity's level for its PoiManager context.
+ Runnable debugPacketTask = () -> {
+ DebugPackets.sendPoiTicketCountPacket(entityLevel, blockPos);
+ };
- DebugPackets.sendPoiTicketCountPacket(level, blockPos);
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) { // Added curly braces here
+ // Schedule POI operations on the POI's level thread, using POI's chunk coordinates for locality
+ poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(blockPos.getX() >> 4, blockPos.getZ() >> 4, poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
+ // Schedule debug packet on the entity's level thread, using entity's chunk coordinates for locality
+ entityLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(entity.chunkPosition().x, entity.chunkPosition().z, debugPacketTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
+ }
+ else { // PWT disabled, run inline on current (entity's) thread
+ poiOperationsTask.run(); // This will use poiLevel's PoiManager but thread checks are permissive
+ debugPacketTask.run(); // This will use entityLevel's PoiManager
+ }
+ // Leaf end - SparklyPaper - parallel world ticking
}
});
entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE);
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 5cc4b2a0d10b25c57c582772ac6757442780afb0..92895a38c5ef7962db83e085a99c0b3766052541 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -795,13 +795,21 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
this.brain.getMemory(moduleType).ifPresent(pos -> {
ServerLevel level = server.getLevel(pos.dimension());
if (level != null) {
- PoiManager poiManager = level.getPoiManager();
- Optional<Holder<PoiType>> type = poiManager.getType(pos.pos());
- BiPredicate<Villager, Holder<PoiType>> biPredicate = POI_MEMORIES.get(moduleType);
- if (type.isPresent() && biPredicate.test(this, type.get())) {
- poiManager.release(pos.pos());
- DebugPackets.sendPoiTicketCountPacket(level, pos.pos());
- }
+ // Leaf start - SparklyPaper - parallel world ticking mod (handling for releasing poi cross-dimension)
+ Runnable releasePoiTask = () -> {
+ PoiManager poiManager = level.getPoiManager();
+ Optional<Holder<PoiType>> type = poiManager.getType(pos.pos());
+ BiPredicate<Villager, Holder<PoiType>> biPredicate = POI_MEMORIES.get(moduleType);
+ if (type.isPresent() && biPredicate.test(this, type.get())) {
+ poiManager.release(pos.pos());
+ DebugPackets.sendPoiTicketCountPacket(level, pos.pos());
+ }
+ };
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ level.moonrise$getChunkTaskScheduler().scheduleChunkTask(0, 0, releasePoiTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
+ else
+ releasePoiTask.run();
+ // Leaf end - SparklyPaper - parallel world ticking mod (handling for releasing poi cross-dimension)
}
});
}
diff --git a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
index 6575e8ef16f6011f7a799ba31531a2ebefee0c4d..a3ee65c9b7544b791f11c0c475ec9e0390be95a7 100644
--- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
+++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
@@ -122,8 +122,10 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
Vec3 vec3 = this.oldPosition();
if (owner instanceof ServerPlayer serverPlayer) {
if (serverPlayer.connection.isAcceptingMessages()) {
+ // Leaf start - SparklyPaper - parallel world ticking mod (handling for pearl teleportation cross-dimension)
+ java.util.function.Consumer<ServerPlayer> teleportPlayerCrossDimensionTask = taskServerPlayer -> {
// CraftBukkit start
- ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL));
+ ServerPlayer serverPlayer1 = taskServerPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL));
if (serverPlayer1 == null) {
this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT);
return;
@@ -152,10 +154,17 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
if (serverPlayer1 != null) {
serverPlayer1.resetFallDistance();
serverPlayer1.resetCurrentImpulseContext();
- serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage
+ serverPlayer1.hurtServer(taskServerPlayer.serverLevel(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage
}
this.playSound(serverLevel, vec3);
+ };
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ serverPlayer.getBukkitEntity().taskScheduler.schedule(teleportPlayerCrossDimensionTask, entity -> {
+ }, 0);
+ else
+ teleportPlayerCrossDimensionTask.accept(serverPlayer);
+ // Leaf end - SparklyPaper - parallel world ticking mod (handling for pearl teleportation cross-dimension)
}
} else {
Entity entity = owner.teleport(
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index c4721eb0efe34f5e313bc890b4e960144eca4fe1..9dd3187fd968ab95e9d55b4c8cc74e782cc0f241 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -96,8 +96,14 @@ public abstract class AbstractContainerMenu {
public void startOpen() {}
// CraftBukkit end
+ public Throwable containerCreationStacktrace; // SparklyPaper - parallel world ticking (debugging)
protected AbstractContainerMenu(@Nullable MenuType<?> menuType, int containerId) {
+ // SparklyPaper - parallel world ticking (debugging)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.logContainerCreationStacktraces) {
+ this.containerCreationStacktrace = new Throwable();
+ }
+ // SparklyPaper end - parallel world ticking (debugging)
this.menuType = menuType;
this.containerId = containerId;
}
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index 07f1b27116baf2a06e6cd4eeaa8639e166fb362d..1a5f65a32882aa43828305113f35a9cdb0bdb870 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -406,8 +406,8 @@ public final class ItemStack implements DataComponentHolder {
if (interactionResult.consumesAction() && serverLevel.captureTreeGeneration && !serverLevel.capturedBlockStates.isEmpty()) {
serverLevel.captureTreeGeneration = false;
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(clickedPos, serverLevel.getWorld());
- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType;
- net.minecraft.world.level.block.SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.getTreeTypeRT(); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(null); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
List<org.bukkit.craftbukkit.block.CraftBlockState> blocks = new java.util.ArrayList<>(serverLevel.capturedBlockStates.values());
serverLevel.capturedBlockStates.clear();
org.bukkit.event.world.StructureGrowEvent structureEvent = null;
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 22da84734462d09a55bc599db4374e1e4f4d6bd2..c8219508cf94da71143b8672661592c66b341782 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -164,6 +164,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
}
// Gale end - Gale configuration
public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files
+ public io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE); // SparklyPaper - parallel world ticking (moved to world)
public static @Nullable BlockPos lastPhysicsProblem; // Spigot
private int tileTickPosition;
@@ -1136,6 +1137,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// CraftBukkit start - tree generation
if (this.captureTreeGeneration) {
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
@@ -1514,9 +1517,12 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
} else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) {
tickingBlockEntity.tick();
// Paper start - rewrite chunk system
- if ((++tickedEntities & 7) == 0) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks();
+ // Leaf start - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - do not bother with condition work / make configurable
+ ++tickedEntities;
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (tickedEntities & 7) == 0) {
+ this.moonrise$midTickTasks();
}
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - do not bother with condition work / make configurable
// Paper end - rewrite chunk system
}
}
@@ -1537,7 +1543,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Gale - Airplane - remove lambda from ticking guard - diff on change ServerLevel#tick
// Paper end - Prevent block entity and entity crashes
}
- this.moonrise$midTickTasks(); // Paper - rewrite chunk system // Gale - Airplane - remove lambda from ticking guard - diff on change ServerLevel#tick
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ this.moonrise$midTickTasks(); // Paper - rewrite chunk system // Gale - Airplane - remove lambda from ticking guard - diff on change ServerLevel#tick
}
// Paper start - Option to prevent armor stands from doing entity lookups
@@ -1680,6 +1687,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@Nullable
public BlockEntity getBlockEntity(BlockPos pos, boolean validate) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // SparklyPaper - parallel world ticking
// Paper start - Perf: Optimize capturedTileEntities lookup
net.minecraft.world.level.block.entity.BlockEntity blockEntity;
if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != null) {
@@ -1697,6 +1706,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
}
public void setBlockEntity(BlockEntity blockEntity) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking
BlockPos blockPos = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockPos)) {
// CraftBukkit start
@@ -1781,6 +1792,8 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
@Override
public List<Entity> getEntities(@Nullable Entity entity, AABB boundingBox, Predicate<? super Entity> predicate) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
List<Entity> list = Lists.newArrayList();
// Paper start - rewrite chunk system
@@ -2099,8 +2112,15 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
public abstract RecipeAccess recipeAccess();
public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) {
- this.randValue = this.randValue * 3 + 1013904223;
- int i = this.randValue >> 2;
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ int i;
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ i = this.random.nextInt() >> 2; // SparklyPaper - parallel world ticking
+ else {
+ this.randValue = this.randValue * 3 + 1013904223;
+ i = this.randValue >> 2;
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
return new BlockPos(x + (i & 15), y + (i >> 16 & yMask), z + (i >> 8 & 15));
}
diff --git a/net/minecraft/world/level/block/FungusBlock.java b/net/minecraft/world/level/block/FungusBlock.java
index 9711efb088bd0da9168e9bcd0496bd7caddd2974..0a8599191bee283ff3a318174f4334e330d466dd 100644
--- a/net/minecraft/world/level/block/FungusBlock.java
+++ b/net/minecraft/world/level/block/FungusBlock.java
@@ -76,9 +76,9 @@ public class FungusBlock extends VegetationBlock implements BonemealableBlock {
// CraftBukkit start
.map((value) -> {
if (this == Blocks.WARPED_FUNGUS) {
- SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS;
+ SaplingBlock.setTreeTypeRT(org.bukkit.TreeType.WARPED_FUNGUS); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
} else if (this == Blocks.CRIMSON_FUNGUS) {
- SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS;
+ SaplingBlock.setTreeTypeRT(org.bukkit.TreeType.CRIMSON_FUNGUS); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
}
return value;
})
diff --git a/net/minecraft/world/level/block/MushroomBlock.java b/net/minecraft/world/level/block/MushroomBlock.java
index d306f5f524dc64618df94c9783c2168dc561a5e3..2e0aec5327d92f89f38bbc393b3ba70597725445 100644
--- a/net/minecraft/world/level/block/MushroomBlock.java
+++ b/net/minecraft/world/level/block/MushroomBlock.java
@@ -93,7 +93,7 @@ public class MushroomBlock extends VegetationBlock implements BonemealableBlock
return false;
} else {
level.removeBlock(pos, false);
- SaplingBlock.treeType = (this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM; // CraftBukkit
+ SaplingBlock.setTreeTypeRT((this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM); // CraftBukkit // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
if (optional.get().value().place(level, level.getChunkSource().getGenerator(), random, pos)) {
return true;
} else {
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..ccc98d71da3c1150878f96801fbf65bfa1a64472 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -65,7 +65,10 @@ public class RedStoneWireBlock extends Block {
private final Function<BlockState, VoxelShape> shapes;
private final BlockState crossState;
private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this);
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
public boolean shouldSignal = true;
+ private final ThreadLocal<Boolean> shouldSignalTL = ThreadLocal.withInitial(() -> true);
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
@Override
public MapCodec<RedStoneWireBlock> codec() {
@@ -283,7 +286,12 @@ public class RedStoneWireBlock extends Block {
if (orientation != null) {
source = pos.relative(orientation.getFront().getOpposite());
}
- turbo.updateSurroundingRedstone(worldIn, pos, state, source);
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source); // SparklyPaper - parallel world ticking
+ else
+ turbo.updateSurroundingRedstone(worldIn, pos, state, source);
+ // Leaf end - parallel world ticking mod (make configurable)
return;
}
updatePowerStrength(worldIn, pos, state, orientation, blockAdded);
@@ -311,7 +319,12 @@ public class RedStoneWireBlock extends Block {
// [Space Walker] suppress shape updates and emit those manually to
// bypass the new neighbor update stack.
if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) {
- turbo.updateNeighborShapes(level, pos, state);
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ level.turbo.updateNeighborShapes(level, pos, state); // SparklyPaper - parallel world ticking
+ else
+ turbo.updateNeighborShapes(level, pos, state);
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
}
}
}
@@ -328,10 +341,19 @@ public class RedStoneWireBlock extends Block {
}
public int getBlockSignal(Level level, BlockPos pos) {
- this.shouldSignal = false;
- int bestNeighborSignal = level.getBestNeighborSignal(pos);
- this.shouldSignal = true;
- return bestNeighborSignal;
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
+ this.shouldSignalTL.set(false);
+ int bestNeighborSignal = level.getBestNeighborSignal(pos);
+ this.shouldSignalTL.set(true);
+ return bestNeighborSignal;
+ } else {
+ this.shouldSignal = false;
+ int bestNeighborSignal = level.getBestNeighborSignal(pos);
+ this.shouldSignal = true;
+ return bestNeighborSignal;
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
}
private void checkCornerChangeAt(Level level, BlockPos pos) {
@@ -422,12 +444,21 @@ public class RedStoneWireBlock extends Block {
@Override
protected int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
- return !this.shouldSignal ? 0 : blockState.getSignal(blockAccess, pos, side);
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ boolean signal = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.shouldSignalTL.get() : this.shouldSignal;
+ return !signal ? 0 : blockState.getSignal(blockAccess, pos, side);
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
}
@Override
protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
- if (this.shouldSignal && side != Direction.DOWN) {
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ boolean signal;
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ signal = this.shouldSignalTL.get();
+ else
+ signal = this.shouldSignal;
+ if (signal && side != Direction.DOWN) {
int powerValue = blockState.getValue(POWER);
if (powerValue == 0) {
return 0;
@@ -441,6 +472,7 @@ public class RedStoneWireBlock extends Block {
return 0;
}
}
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
protected static boolean shouldConnectTo(BlockState state) {
return shouldConnectTo(state, null);
@@ -459,7 +491,12 @@ public class RedStoneWireBlock extends Block {
@Override
protected boolean isSignalSource(BlockState state) {
- return this.shouldSignal;
+ // Leaf start - SparklyPaper - parallel world ticking mod (make configurable)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ return this.shouldSignalTL.get();
+ else
+ return this.shouldSignal;
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
}
public static int getColorForPower(int power) {
diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java
index a22cb810622e0ae97bc2a0d6390d026d9482b783..3e7478e959da3a0191de6c76b80cbb9b625b6898 100644
--- a/net/minecraft/world/level/block/SaplingBlock.java
+++ b/net/minecraft/world/level/block/SaplingBlock.java
@@ -26,6 +26,28 @@ public class SaplingBlock extends VegetationBlock implements BonemealableBlock {
private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 12.0);
protected final TreeGrower treeGrower;
public static @javax.annotation.Nullable org.bukkit.TreeType treeType; // CraftBukkit
+ public static final ThreadLocal<org.bukkit.TreeType> treeTypeRT = new ThreadLocal<>(); // SparklyPaper - parallel world ticking (from Folia)
+
+ // Leaf start - SparklyPaper - parallel world ticking mod
+ // refer to original field in case plugins attempt to modify it
+ public static org.bukkit.TreeType getTreeTypeRT() {
+ org.bukkit.TreeType treeTypeRTCopy;
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (treeTypeRTCopy = treeTypeRT.get()) != null)
+ return treeTypeRTCopy;
+ synchronized (SaplingBlock.class) {
+ return treeType;
+ }
+ }
+
+ // update original field in case plugins attempt to access it
+ public static void setTreeTypeRT(org.bukkit.TreeType value) {
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled)
+ treeTypeRT.set(value);
+ synchronized (SaplingBlock.class) {
+ treeType = value;
+ }
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod
@Override
public MapCodec<? extends SaplingBlock> codec() {
@@ -62,14 +84,14 @@ public class SaplingBlock extends VegetationBlock implements BonemealableBlock {
this.treeGrower.growTree(level, level.getChunkSource().getGenerator(), pos, state, random);
level.captureTreeGeneration = false;
if (!level.capturedBlockStates.isEmpty()) {
- org.bukkit.TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null;
+ org.bukkit.TreeType treeTypeLocal = 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)
org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level.getWorld());
java.util.List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(level.capturedBlockStates.values());
level.capturedBlockStates.clear();
org.bukkit.event.world.StructureGrowEvent event = null;
- if (treeType != null) {
- event = new org.bukkit.event.world.StructureGrowEvent(location, treeType, false, null, blocks);
+ if (treeTypeLocal != null) { // Leaf - SparklyPaper - parallel world ticking mod (fix missing stuff from original sparkly paper)
+ event = new org.bukkit.event.world.StructureGrowEvent(location, treeTypeLocal, false, null, blocks); // Leaf - SparklyPaper - parallel world ticking mod (fix missing stuff from original sparkly paper)
org.bukkit.Bukkit.getPluginManager().callEvent(event);
}
if (event == null || !event.isCancelled()) {
diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
index c63370fd458fb4f7190b79b1a8174fcc92d88f9c..5d3d3db955927647f5498eac0d67d3198b27f8cd 100644
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
@@ -79,6 +79,12 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
}
public static boolean canUnlock(Player player, LockCode code, Component displayName, @Nullable BlockEntity blockEntity) {
+ // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != serverPlayer.serverLevel()) { // Leaf - SparklyPaper - parallel world ticking mod (make configurable)
+ net.minecraft.server.MinecraftServer.LOGGER.warn("Player " + serverPlayer.getScoreboardName() + " (" + serverPlayer.getStringUUID() + ") attempted to open a BlockEntity @ " + blockEntity.getLevel().getWorld().getName() + " " + blockEntity.getBlockPos().getX() + ", " + blockEntity.getBlockPos().getY() + ", " + blockEntity.getBlockPos().getZ() + " while they were in a different world " + serverPlayer.level().getWorld().getName() + " than the block themselves!");
+ return false;
+ }
+ // SparklyPaper end - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != null && blockEntity.getLevel().getBlockEntity(blockEntity.getBlockPos()) == blockEntity) {
final org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos());
net.kyori.adventure.text.Component lockedMessage = net.kyori.adventure.text.Component.translatable("container.isLocked", io.papermc.paper.adventure.PaperAdventure.asAdventure(displayName));
diff --git a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
index 440d70811d96f97d3463c6aff131cbc5bd588254..a3178392325ca186f1c68143df7d1f499f6e762c 100644
--- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
@@ -43,9 +43,9 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi
// Paper end - Fix NPE in SculkBloomEvent world access
public static void serverTick(Level level, BlockPos pos, BlockState state, SculkCatalystBlockEntity sculkCatalyst) {
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = sculkCatalyst.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
+ org.bukkit.craftbukkit.event.CraftEventFactory.setSourceBlockOverrideRT(pos); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep. // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
sculkCatalyst.catalystListener.getSculkSpreader().updateCursors(level, pos, level.getRandom(), true);
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit
+ org.bukkit.craftbukkit.event.CraftEventFactory.setSourceBlockOverrideRT(null); // CraftBukkit // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
}
@Override
diff --git a/net/minecraft/world/level/block/grower/TreeGrower.java b/net/minecraft/world/level/block/grower/TreeGrower.java
index d23f255de9208f42125fa358a9e8194c984fe4d3..c3acc0a875c0bf697b6b101051d80b3457431b92 100644
--- a/net/minecraft/world/level/block/grower/TreeGrower.java
+++ b/net/minecraft/world/level/block/grower/TreeGrower.java
@@ -203,55 +203,57 @@ public final class TreeGrower {
// CraftBukkit start
private void setTreeType(Holder<ConfiguredFeature<?, ?>> feature) {
+ org.bukkit.TreeType treeType; // SparklyPaper - parallel world ticking
if (feature.is(TreeFeatures.OAK) || feature.is(TreeFeatures.OAK_BEES_005)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TREE;
+ treeType = org.bukkit.TreeType.TREE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.HUGE_RED_MUSHROOM)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_MUSHROOM;
+ treeType = org.bukkit.TreeType.RED_MUSHROOM; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.HUGE_BROWN_MUSHROOM)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BROWN_MUSHROOM;
+ treeType = org.bukkit.TreeType.BROWN_MUSHROOM; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.JUNGLE_TREE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.COCOA_TREE;
+ treeType = org.bukkit.TreeType.COCOA_TREE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.JUNGLE_TREE_NO_VINE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SMALL_JUNGLE;
+ treeType = org.bukkit.TreeType.SMALL_JUNGLE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.PINE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD;
+ treeType = org.bukkit.TreeType.TALL_REDWOOD; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.SPRUCE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD;
+ treeType = org.bukkit.TreeType.REDWOOD; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.ACACIA)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA;
+ treeType = org.bukkit.TreeType.ACACIA; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.BIRCH) || feature.is(TreeFeatures.BIRCH_BEES_005)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIRCH;
+ treeType = org.bukkit.TreeType.BIRCH; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.SUPER_BIRCH_BEES_0002)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_BIRCH;
+ treeType = org.bukkit.TreeType.TALL_BIRCH; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.SWAMP_OAK)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP;
+ treeType = org.bukkit.TreeType.SWAMP; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.FANCY_OAK) || feature.is(TreeFeatures.FANCY_OAK_BEES_005)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIG_TREE;
+ treeType = org.bukkit.TreeType.BIG_TREE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.JUNGLE_BUSH)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE_BUSH;
+ treeType = org.bukkit.TreeType.JUNGLE_BUSH; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.DARK_OAK)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.DARK_OAK;
+ treeType = org.bukkit.TreeType.DARK_OAK; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.MEGA_SPRUCE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_REDWOOD;
+ treeType = org.bukkit.TreeType.MEGA_REDWOOD; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.MEGA_PINE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_PINE;
+ treeType = org.bukkit.TreeType.MEGA_PINE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE;
+ treeType = org.bukkit.TreeType.JUNGLE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.AZALEA_TREE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA;
+ treeType = org.bukkit.TreeType.AZALEA; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.MANGROVE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE;
+ treeType = org.bukkit.TreeType.MANGROVE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.TALL_MANGROVE)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_MANGROVE;
+ treeType = org.bukkit.TreeType.TALL_MANGROVE; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.CHERRY) || feature.is(TreeFeatures.CHERRY_BEES_005)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.CHERRY;
+ treeType = org.bukkit.TreeType.CHERRY; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.PALE_OAK) || feature.is(TreeFeatures.PALE_OAK_BONEMEAL)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK;
+ treeType = org.bukkit.TreeType.PALE_OAK; // SparklyPaper - parallel world ticking
} else if (feature.is(TreeFeatures.PALE_OAK_CREAKING)) {
- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK_CREAKING;
+ treeType = org.bukkit.TreeType.PALE_OAK_CREAKING; // SparklyPaper - parallel world ticking
} else {
throw new IllegalArgumentException("Unknown tree generator " + feature);
}
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(treeType); // SparklyPaper - parallel world ticking // Leaf - SparklyPaper - parallel world ticking mod (collapse original behavior)
}
// CraftBukkit end
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 5325989f46bd288c5a6f1362ec17d3354d55abfd..8d8d909c4b9496ae6f44ba34203ee40433d37f8c 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -399,6 +399,8 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
@Nullable
@Override
public BlockState setBlockState(BlockPos pos, BlockState state, int flags) {
+ 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.level, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
int y = pos.getY();
LevelChunkSection section = this.getSection(this.getSectionIndex(y));
boolean hasOnlyAir = section.hasOnlyAir();
diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java
index dec51066fc3f57b7bdc56195313c219f45a7fbee..359ead3bd32862206bccb4504ad5b4d027dba7a9 100644
--- a/net/minecraft/world/level/entity/EntityTickList.java
+++ b/net/minecraft/world/level/entity/EntityTickList.java
@@ -11,16 +11,36 @@ import net.minecraft.world.entity.Entity;
public class EntityTickList {
public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<net.minecraft.world.entity.Entity> entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system // Pufferfish - private->public
+ // Leaf start - SparklyPaper - parallel world ticking mod
+ // preserve original constructor
+ public EntityTickList() {
+ this(null);
+ }
+ // Leaf end - SparklyPaper - parallel world ticking mod
+
+ // SparklyPaper start - parallel world ticking
+ // Used to track async entity additions/removals/loops
+ @Nullable // Leaf - SparklyPaper - parallel world ticking mod (preserve original constructor)
+ private final net.minecraft.server.level.ServerLevel serverLevel;
+ public EntityTickList(@Nullable net.minecraft.server.level.ServerLevel serverLevel) { // Leaf - SparklyPaper - parallel world ticking mod (preserve original constructor)
+ this.serverLevel = serverLevel;
+ }
+ // SparklyPaper end - parallel world ticking
+
private void ensureActiveIsNotIterated() {
// Paper - rewrite chunk system
}
public void add(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(entity, "Asynchronous entity ticklist addition"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.ensureActiveIsNotIterated();
this.entities.add(entity); // Paper - rewrite chunk system
}
public void remove(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(entity, "Asynchronous entity ticklist removal"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
this.ensureActiveIsNotIterated();
this.entities.remove(entity); // Paper - rewrite chunk system
}
@@ -30,6 +50,8 @@ public class EntityTickList {
}
public void forEach(Consumer<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(serverLevel, "Asynchronous entity ticklist iteration"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
// Paper start - rewrite chunk system
// To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
// (by dfl iterator() is configured to not iterate over new entries)
diff --git a/net/minecraft/world/level/saveddata/maps/MapIndex.java b/net/minecraft/world/level/saveddata/maps/MapIndex.java
index 06025d79cc2297119b22224d777aca79f9d3d9c1..8e70deae0ff70d21e31f54410e3886be0280575c 100644
--- a/net/minecraft/world/level/saveddata/maps/MapIndex.java
+++ b/net/minecraft/world/level/saveddata/maps/MapIndex.java
@@ -23,8 +23,10 @@ public class MapIndex extends SavedData {
}
public MapId getNextMapId() {
+ synchronized (TYPE) { // SparklyPaper start - parallel world ticking
MapId mapId = new MapId(++this.lastMapId);
this.setDirty();
return mapId;
+ } // SparklyPaper end - parallel world ticking
}
}