mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2026-01-04 15:41:40 +00:00
* Unify comment format * More configurable * Remove one extra execute mid-tick task call in level tick when PWT is disabled This may cause extremely rare, weird, strange, magic, mysterious issues with plugins, or potentially more. One example is that it may cause boss mob duplication issue when `ONE MOB ONLY` was enabled in plugin SupremeBosses
1585 lines
107 KiB
Diff
1585 lines
107 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
|
Date: Sun, 25 May 2025 21:39:32 -0300
|
|
Subject: [PATCH] SparklyPaper: Parallel world ticking
|
|
|
|
Original project: https://github.com/SparklyPower/SparklyPaper
|
|
|
|
Co-authored-by: Altiami <yoshimo.kristin@gmail.com>
|
|
Co-authored-by: Taiyou06 <kaandindar21@gmail.com>
|
|
Co-authored-by: MrlingXD <90316914+wling-art@users.noreply.github.com>
|
|
|
|
Commit: e2e154fb764650c90bbd23c9cede2b11ba1a08e2
|
|
|
|
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
index 0f5966932c4211922eccac09ab164fcb69dad582..f1e1e96a425ae0186164b3ae6caca226fdd7e12e 100644
|
|
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
@@ -1142,7 +1142,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()) { // Leaf - SparklyPaper - parallel world ticking
|
|
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
|
|
// which will invoke processTicketUpdates
|
|
this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
|
|
@@ -1163,7 +1163,13 @@ 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
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ TickThread.ensureTickThread(world, "Cannot unload chunks off-main");
|
|
+ } else {
|
|
+ TickThread.ensureTickThread("Cannot unload chunks off-main");
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
|
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
|
throw new IllegalStateException("Cannot unload chunks recursively");
|
|
@@ -1429,7 +1435,7 @@ public final class ChunkHolderManager {
|
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
|
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
|
|
}
|
|
- final boolean isTickThread = TickThread.isTickThread();
|
|
+ final boolean isTickThread = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? TickThread.isTickThreadFor(world) : TickThread.isTickThread(); // Leaf - SparklyPaper - parallel world ticking
|
|
|
|
if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) {
|
|
TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
|
|
diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java
|
|
index ea01daf583ddd110f153304a6a65ac2d765b9d31..b106bbc9334ac345390592d6e152edef1d317ed2 100644
|
|
--- a/io/papermc/paper/entity/activation/ActivationRange.java
|
|
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
|
|
@@ -134,7 +134,7 @@ public final class ActivationRange {
|
|
*
|
|
* @param world
|
|
*/
|
|
- public static void activateEntities(final Level world) {
|
|
+ public synchronized static void activateEntities(final Level world) { // Leaf - SparklyPaper - parallel world ticking
|
|
final int miscActivationRange = world.spigotConfig.miscActivationRange;
|
|
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
|
|
final int animalActivationRange = world.spigotConfig.animalActivationRange;
|
|
diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java
|
|
index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..0838b3d60e94a280a6fdd1aef413078a0ec71a1c 100644
|
|
--- a/io/papermc/paper/redstone/RedstoneWireTurbo.java
|
|
+++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java
|
|
@@ -829,14 +829,22 @@ 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
|
|
+ final int k;
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ // This now correctly calls the (conditionally) thread-safe method in RedStoneWireBlock
|
|
+ k = wire.getBlockSignal(worldIn, upd.self);
|
|
+ } else {
|
|
+ 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.
|
|
+ k = worldIn.getBestNeighborSignal(upd.self);
|
|
+ wire.shouldSignal = true;
|
|
+ }
|
|
+ // 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..e90201f4d878874e380d12d82220fc6cd1df9b9c 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(); // Leaf - SparklyPaper - parallel world ticking
|
|
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(null); // Leaf - SparklyPaper - parallel world ticking
|
|
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 f8dd55a4a5a93788e0690b1a6a6b87d12cf26571..51efe9aadb377c35d6ed2b7eadead3c9e1c745f8 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 gg.pufferfish.pufferfish.util.AsyncExecutor mobSpawnExecutor = new gg.pufferfish.pufferfish.util.AsyncExecutor("Leaf Async Mob Spawn Thread"); // Pufferfish - optimize mob spawning // Leaf - Fix Pufferfish and Purpur patches - Unify thread name
|
|
+ public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // Leaf - SparklyPaper - parallel world ticking
|
|
|
|
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
|
|
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
|
|
@@ -322,24 +323,35 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
private long lastMidTickExecute;
|
|
private long lastMidTickExecuteFailure;
|
|
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ private boolean pwt$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
|
|
+
|
|
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 - 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.pwt$tickLevelMidTickTasks(levelThread.currentTickingServerLevel);
|
|
+ } else {
|
|
+ boolean executed = false;
|
|
+ for (final ServerLevel world : this.getAllLevels()) {
|
|
+ executed = executed || this.pwt$tickLevelMidTickTasks(world);
|
|
}
|
|
+ return executed;
|
|
}
|
|
-
|
|
- return executed;
|
|
+ // Leaf end - SparklyPaper - parallel world ticking - only mid-tick the level for the current thread
|
|
}
|
|
|
|
@Override
|
|
@@ -917,6 +929,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;
|
|
}
|
|
|
|
@@ -1665,6 +1683,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
public final io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList entitySchedulerTickList = new io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList(); // Paper - optimise Folia entity scheduler
|
|
|
|
+ // Leaf start - SparklyPaper - parallel world ticking - 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 - 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
|
|
@@ -1710,28 +1740,49 @@ 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 */
|
|
+ // Leaf start - SparklyPaper - 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);
|
|
+ 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.currentTickingServerLevel = serverLevel;
|
|
+
|
|
+ try {
|
|
+ tickLevel(serverLevel, hasTimeLeft); // Leaf - SparklyPaper - parallel world ticking - move level ticking logic out for branch convergence
|
|
+ } finally {
|
|
+ serverLevelTickingSemaphore.release();
|
|
+ }
|
|
+ }, serverLevel)
|
|
+ );
|
|
+ } else {
|
|
+ tickLevel(serverLevel, hasTimeLeft);
|
|
+ }
|
|
+
|
|
+ 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
|
|
}
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
|
|
|
if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { org.dreeam.leaf.async.tracker.AsyncTracker.onTickEnd(this); } // Leaf - Multithreaded tracker
|
|
@@ -1817,6 +1868,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
level.trackerTask = null;
|
|
}
|
|
// Leaf end - Multithreaded tracker
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) level.tickExecutor.shutdown(); // Leaf - 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 4bf87ebb49880b8e09203a48fce6371398281561..500fbe40dfcafe045199eb4bc39af5115d57ac9a 100644
|
|
--- a/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -53,8 +53,10 @@ 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 = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? java.util.concurrent.ConcurrentHashMap.newKeySet() : new HashSet<>();
|
|
+ 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;
|
|
@@ -150,7 +152,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);
|
|
}
|
|
});
|
|
@@ -224,7 +226,7 @@ public class PlayerAdvancements {
|
|
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);
|
|
}
|
|
|
|
@@ -270,6 +272,7 @@ public class PlayerAdvancements {
|
|
}
|
|
|
|
public void flushDirty(ServerPlayer player, boolean showAdvancements) {
|
|
+ final boolean isConcurrent = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled; // Leaf - SparklyPaper - parallel world ticking
|
|
if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.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.
|
|
@@ -281,13 +284,23 @@ public class PlayerAdvancements {
|
|
|
|
this.rootsToUpdate.clear();
|
|
|
|
- for (AdvancementHolder advancementHolder : this.progressChanged) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ if (!this.progressChanged.isEmpty()) {
|
|
+ Set<AdvancementHolder> toProcess = isConcurrent ? new HashSet<>(this.progressChanged) : this.progressChanged;
|
|
+
|
|
+ for (AdvancementHolder advancementHolder : toProcess) {
|
|
if (this.visible.contains(advancementHolder)) {
|
|
map.put(advancementHolder.id(), this.progress.get(advancementHolder));
|
|
}
|
|
}
|
|
|
|
- this.progressChanged.clear();
|
|
+ if (isConcurrent) {
|
|
+ this.progressChanged.removeAll(toProcess); // Remove processed items from concurrent set
|
|
+ } else {
|
|
+ this.progressChanged.clear();
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) {
|
|
player.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map, showAdvancements));
|
|
}
|
|
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index 8350d6f00764c792f5f4f2c2e91df6777cb06e0d..2139fc5c3c3e6b45e148d6f8798b9fbfcdef626c 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
|
|
org.dreeam.leaf.command.LeafCommands.registerCommands(this); // Leaf - Leaf commands
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.threads);
|
|
+ DedicatedServer.LOGGER.info("Using {} permits for parallel world ticking", serverLevelTickingSemaphore.availablePermits());
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
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 eaaa66c4d86d4ebda0acf8f1dbe8ecb55aa28285..908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4 100644
|
|
--- a/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -175,6 +175,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
|
|
// call mid-tick tasks for chunk system
|
|
if ((i & 7) == 0) {
|
|
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
|
|
continue;
|
|
}
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index 30bb5b9d021d01dea272313763b0748856ce28a6..f1ce73c615f1804c1f44f140dc17965877937078 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -180,7 +180,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); // Leaf - SparklyPaper - parallel world ticking
|
|
private final ServerWaypointManager waypointManager;
|
|
// Paper - rewrite chunk system
|
|
private final GameEventDispatcher gameEventDispatcher;
|
|
@@ -207,6 +207,11 @@ 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;
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ public final java.util.concurrent.ExecutorService tickExecutor;
|
|
+ public final java.util.concurrent.ConcurrentLinkedQueue<org.dreeam.leaf.async.world.WorldReadRequest> asyncReadRequestQueue;
|
|
+ private volatile boolean isShuttingDown = false; // Shutdown handling for async reads
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
|
|
// CraftBukkit start
|
|
public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
|
|
@@ -680,7 +685,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.sleepStatus = new SleepStatus();
|
|
this.gameEventDispatcher = new GameEventDispatcher(this);
|
|
this.randomSequences = Objects.requireNonNullElseGet(randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.TYPE));
|
|
- this.waypointManager = new ServerWaypointManager();
|
|
+ this.waypointManager = new ServerWaypointManager(this); // Leaf - SparklyPaper - parallel world ticking
|
|
// Paper start - rewrite chunk system
|
|
this.moonrise$setEntityLookup(new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup((ServerLevel)(Object)this, ((ServerLevel)(Object)this).new EntityCallbacks()));
|
|
this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this);
|
|
@@ -698,6 +703,15 @@ 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
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.dreeam.leaf.async.world.SparklyPaperServerLevelTickExecutorThreadFactory(getWorld().getName()));
|
|
+ this.asyncReadRequestQueue = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
|
+ } else {
|
|
+ this.tickExecutor = null;
|
|
+ this.asyncReadRequestQueue = null;
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
}
|
|
|
|
// Paper start
|
|
@@ -730,8 +744,131 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
return this.structureManager;
|
|
}
|
|
|
|
+ // 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 {} pending async read requests for world {} during shutdown.", clearedRequests, this.getWorld().getName());
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking - Shutdown handling for async reads
|
|
+
|
|
+ // 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
|
|
+ public 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) {
|
|
@@ -739,6 +876,12 @@ 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.tickedBlocksOrFluids & 7L) != 0L) { // Execute mid-tick tasks if PWT is enabled
|
|
+ this.moonrise$midTickTasks();
|
|
+ }
|
|
+ // 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
|
|
@@ -831,6 +974,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
|
|
// Paper end - Prevent block entity and entity crashes
|
|
}
|
|
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
this.moonrise$midTickTasks(); // Paper - rewrite chunk system
|
|
// Gale end - Airplane - remove lambda from ticking guard - copied from guardEntityTick
|
|
}
|
|
@@ -1311,7 +1455,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
fluidState.tick(this, pos, blockState);
|
|
}
|
|
// Paper start - rewrite chunk system
|
|
- if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
|
|
+ // 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) {
|
|
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -1324,7 +1471,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
blockState.tick(this, pos, this.random);
|
|
}
|
|
// Paper start - rewrite chunk system
|
|
- if ((++this.tickedBlocksOrFluids & 7L) != 0L) {
|
|
+ // 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) {
|
|
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks();
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -1591,6 +1741,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
}
|
|
|
|
private void addPlayer(ServerPlayer player) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // Leaf - SparklyPaper - parallel world ticking (async is no longer safe; schedule on main; additional concurrency issues logs)
|
|
Entity entity = this.getEntity(player.getUUID());
|
|
if (entity != null) {
|
|
LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
|
|
@@ -1603,7 +1754,13 @@ 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
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add entity off-main"); // Leaf - SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
+ } else {
|
|
+ org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
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 b8f5cc4540c4a992ae4cf5673886ce6107eb82a8..b8c59df25999183985538ade4e58e01eb9b5b586 100644
|
|
--- a/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -463,6 +463,8 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
|
|
+ public boolean hasTickedAtLeastOnceInNewWorld = false; // Leaf - 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, gameProfile);
|
|
this.textFilter = server.createTextFilterForPlayer(this);
|
|
@@ -749,6 +751,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ hasTickedAtLeastOnceInNewWorld = true; // Leaf - SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
// CraftBukkit start
|
|
if (this.joining) {
|
|
this.joining = false;
|
|
@@ -1438,6 +1441,7 @@ 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) 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() + "]"); // Leaf - SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// CraftBukkit start
|
|
/*
|
|
this.isChangingDimension = true;
|
|
@@ -1784,6 +1788,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
|
return OptionalInt.empty();
|
|
} else {
|
|
// CraftBukkit start
|
|
+ // Leaf start - SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && !hasTickedAtLeastOnceInNewWorld) {
|
|
+ MinecraftServer.LOGGER.warn("Ignoring request to open container {} because we haven't ticked in the current world yet!", abstractContainerMenu, new Throwable());
|
|
+ return OptionalInt.empty();
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
this.containerMenu = abstractContainerMenu; // Moved up
|
|
if (!this.isImmobile())
|
|
this.connection
|
|
@@ -1848,6 +1858,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
|
}
|
|
@Override
|
|
public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking (debugging)
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.logContainerCreationStacktraces) {
|
|
+ MinecraftServer.LOGGER.warn("Closing {} inventory that was created at", this.getBukkitEntity().getName(), this.containerMenu.containerCreationStacktrace);
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - 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 837fed1c7786d6d3397f70d910cace4cdf2223ce..8f5d037fcd307b88abfae13e590e987200114b8a 100644
|
|
--- a/net/minecraft/server/players/PlayerList.java
|
|
+++ b/net/minecraft/server/players/PlayerList.java
|
|
@@ -257,6 +257,7 @@ 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) ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // Leaf - SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
player.isRealPlayer = true; // Paper
|
|
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
|
|
GameProfile gameProfile = player.getGameProfile();
|
|
@@ -852,6 +853,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
|
|
+ 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
|
|
player.stopRiding(); // CraftBukkit
|
|
this.players.remove(player);
|
|
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
|
|
@@ -863,6 +873,7 @@ public abstract class PlayerList {
|
|
ServerPlayer serverPlayer = player;
|
|
Level fromWorld = player.level();
|
|
player.wonGame = false;
|
|
+ serverPlayer.hasTickedAtLeastOnceInNewWorld = false; // Leaf - 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/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
|
index f9e7532f86122a379692561a639a209a126e8bba..7716c491a053db8b18aa23a4c2c768bc6971f9a9 100644
|
|
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
|
|
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
|
@@ -20,8 +20,16 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
private final Set<ServerPlayer> players = new HashSet<>();
|
|
private final Table<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = HashBasedTable.create();
|
|
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ private final net.minecraft.server.level.ServerLevel level;
|
|
+ public ServerWaypointManager(net.minecraft.server.level.ServerLevel level) {
|
|
+ this.level = level;
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
+
|
|
@Override
|
|
public void trackWaypoint(WaypointTransmitter waypoint) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot track waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
this.waypoints.add(waypoint);
|
|
|
|
for (ServerPlayer serverPlayer : this.players) {
|
|
@@ -31,6 +39,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
|
|
@Override
|
|
public void updateWaypoint(WaypointTransmitter waypoint) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
if (this.waypoints.contains(waypoint)) {
|
|
Map<ServerPlayer, WaypointTransmitter.Connection> map = Tables.transpose(this.connections).row(waypoint);
|
|
SetView<ServerPlayer> set = Sets.difference(this.players, map.keySet());
|
|
@@ -47,12 +56,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
|
|
@Override
|
|
public void untrackWaypoint(WaypointTransmitter waypoint) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot untrack waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
this.connections.column(waypoint).forEach((serverPlayer, connection) -> connection.disconnect());
|
|
Tables.transpose(this.connections).row(waypoint).clear();
|
|
this.waypoints.remove(waypoint);
|
|
}
|
|
|
|
public void addPlayer(ServerPlayer player) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot add player to waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
this.players.add(player);
|
|
|
|
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
|
|
@@ -65,6 +76,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
}
|
|
|
|
public void updatePlayer(ServerPlayer player) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update player for waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
|
|
SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
|
|
|
|
@@ -78,6 +90,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
}
|
|
|
|
public void removePlayer(ServerPlayer player) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot remove player from waypoints off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
this.connections.row(player).values().removeIf(connection -> {
|
|
connection.disconnect();
|
|
return true;
|
|
@@ -87,6 +100,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
}
|
|
|
|
public void breakAllConnections() {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot break all waypoint connections off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
this.connections.values().forEach(WaypointTransmitter.Connection::disconnect);
|
|
this.connections.clear();
|
|
}
|
|
@@ -106,6 +120,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
}
|
|
|
|
private void createConnection(ServerPlayer player, WaypointTransmitter waypoint) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot create waypoint connections off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
if (player != waypoint) {
|
|
if (isLocatorBarEnabledFor(player)) {
|
|
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection -> {
|
|
@@ -122,6 +137,7 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
|
}
|
|
|
|
private void updateConnection(ServerPlayer player, WaypointTransmitter waypoint, WaypointTransmitter.Connection connection) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot update waypoint connection off-main"); // Leaf - SparklyPaper - parallel world ticking
|
|
if (player != waypoint) {
|
|
if (isLocatorBarEnabledFor(player)) {
|
|
if (!connection.isBroken()) {
|
|
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
|
index 67556752551e1d30519e2533543de9b04342420b..97f8e0e7fc18a465ed3b7ee933c8e83eb95d83ab 100644
|
|
--- a/net/minecraft/world/entity/Entity.java
|
|
+++ b/net/minecraft/world/entity/Entity.java
|
|
@@ -3476,15 +3476,7 @@ 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);
|
|
- }
|
|
- }
|
|
-
|
|
+ handleTeleport(serverLevel); // Leaf - SparklyPaper - parallel world ticking
|
|
} else if (this.portalProcess.hasExpired()) {
|
|
this.portalProcess = null;
|
|
}
|
|
@@ -3492,6 +3484,56 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - SparklyPaper - parallel world ticking - mark pending teleport to prevent clearing portal process
|
|
+ private void handleTeleport(ServerLevel serverLevel) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ pwt$handleTeleport(serverLevel);
|
|
+ } else {
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void pwt$handleTeleport(ServerLevel serverLevel) {
|
|
+ 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();
|
|
+ }
|
|
+ };
|
|
+
|
|
+ this.portalProcess.setParallelAsScheduled();
|
|
+ this.getBukkitEntity().taskScheduler.schedule(portalEntityTask, entity -> {
|
|
+ }, 0);
|
|
+ }
|
|
+ // Leaf start - SparklyPaper - parallel world ticking - mark pending teleport to prevent clearing portal process
|
|
+
|
|
public int getDimensionChangingDelay() {
|
|
Entity firstPassenger = this.getFirstPassenger();
|
|
return firstPassenger instanceof ServerPlayer ? firstPassenger.getDimensionChangingDelay() : 300;
|
|
@@ -4061,6 +4103,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
}
|
|
|
|
private Entity teleportCrossDimension(ServerLevel oldLevel, ServerLevel newLevel, TeleportTransition teleportTransition) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(newLevel, "Cannot teleport entity to another world off-main, from world[" + oldLevel.getWorld().getName() + "] to world[" + newLevel.getWorld().getName() + "]"); // Leaf - 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..94da2272f402b8269aaae2983f4cc54bdf07caa0 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 - 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 - 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 - 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 - 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 - 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..2127bd4f9dfcbba0e073375d17399afce71c2060 100644
|
|
--- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
|
|
+++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java
|
|
@@ -44,15 +44,39 @@ 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
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ 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);
|
|
+
|
|
+ // 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 {
|
|
+ ServerLevel level1 = level.getServer().getLevel(globalPos.dimension());
|
|
+ if (level1 != null) {
|
|
+ PoiManager poiManager = level1.getPoiManager();
|
|
+ if (poiManager.exists(blockPos, holder -> true)) {
|
|
+ poiManager.release(blockPos);
|
|
+ }
|
|
|
|
- DebugPackets.sendPoiTicketCountPacket(level, blockPos);
|
|
+ DebugPackets.sendPoiTicketCountPacket(level, blockPos);
|
|
+ }
|
|
}
|
|
+ // 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 2b20cd647ec00b3503cf5634dfbe9b9a8f8edbc4..bcce0c38f476966c9789506cb6a83e2e1ce24cc4 100644
|
|
--- a/net/minecraft/world/entity/npc/Villager.java
|
|
+++ b/net/minecraft/world/entity/npc/Villager.java
|
|
@@ -794,13 +794,29 @@ 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 - handling for releasing poi cross-dimension
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ 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());
|
|
+ }
|
|
+ };
|
|
+
|
|
+ level.moonrise$getChunkTaskScheduler().scheduleChunkTask(0, 0, releasePoiTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING);
|
|
+ } else {
|
|
+ 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 end - SparklyPaper - parallel world ticking - 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 ebc7db0fc6e8fb8f4bd19945e61287b2ff61cdbc..d1875d36ccdad96aabfe275237984f47cdc4d555 100644
|
|
--- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
+++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
|
|
@@ -119,11 +119,13 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
Vec3 vec3 = this.oldPosition();
|
|
if (owner instanceof ServerPlayer serverPlayer) {
|
|
if (serverPlayer.connection.isAcceptingMessages()) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking - handling for pearl teleportation cross-dimension
|
|
+ java.util.function.Consumer<ServerPlayer> teleportPlayerCrossDimensionTask = taskServerPlayer -> {
|
|
// CraftBukkit start
|
|
// Store pre teleportation position as the teleport has been moved up.
|
|
final double preTeleportX = serverPlayer.getX(), preTeleportY = serverPlayer.getY(), preTeleportZ = serverPlayer.getZ();
|
|
final float preTeleportYRot = serverPlayer.getYRot(), preTeleportXRot = serverPlayer.getXRot();
|
|
- 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,18 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
|
|
if (serverPlayer1 != null) {
|
|
serverPlayer1.resetFallDistance();
|
|
serverPlayer1.resetCurrentImpulseContext();
|
|
- serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage
|
|
+ serverPlayer1.hurtServer(taskServerPlayer.level(), 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 - 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 4354aafdd29c397d1318ae71dc365e7ca0aa781c..89600b750e85011e6cf39b2d5559111d22735e9c 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; // Leaf - SparklyPaper - parallel world ticking (debugging)
|
|
|
|
protected AbstractContainerMenu(@Nullable MenuType<?> menuType, int containerId) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking (debugging)
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.logContainerCreationStacktraces) {
|
|
+ this.containerCreationStacktrace = new Throwable();
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - 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 8c713d90e81df61d65fa6770516afc4704bbbb6f..e17b3b10f665f7b3d5f607838723071a7e0bfc59 100644
|
|
--- a/net/minecraft/world/item/ItemStack.java
|
|
+++ b/net/minecraft/world/item/ItemStack.java
|
|
@@ -398,8 +398,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(); // Leaf - SparklyPaper - parallel world ticking
|
|
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(null); // Leaf - SparklyPaper - parallel world ticking
|
|
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 adf9d0bc98408477450bcee8628b91fda2b95c54..779ed937e35ebf10ce49a04bde31cd78de264ba4 100644
|
|
--- a/net/minecraft/world/level/Level.java
|
|
+++ b/net/minecraft/world/level/Level.java
|
|
@@ -166,6 +166,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 final io.papermc.paper.redstone.RedstoneWireTurbo turbo; // Leaf - SparklyPaper - parallel world ticking - moved to world
|
|
public static @Nullable BlockPos lastPhysicsProblem; // Spigot
|
|
private int tileTickPosition;
|
|
public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions
|
|
@@ -968,6 +969,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
|
// CraftBukkit end
|
|
this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
|
|
this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system
|
|
+ this.turbo = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? new io.papermc.paper.redstone.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE) : null;
|
|
}
|
|
|
|
// Paper start - Cancel hit for vanished players
|
|
@@ -1138,6 +1140,7 @@ 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) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, pos, "Updating block asynchronously"); // Leaf - 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
|
|
@@ -1516,7 +1519,10 @@ 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) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
+ ++tickedEntities;
|
|
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled && (tickedEntities & 7) == 0) {
|
|
+ // Leaf end - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks();
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -1539,6 +1545,7 @@ 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
|
|
}
|
|
+ if (!org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) // Leaf - SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
this.moonrise$midTickTasks(); // Paper - rewrite chunk system // Gale - Airplane - remove lambda from ticking guard - diff on change ServerLevel#tick
|
|
}
|
|
|
|
@@ -1676,6 +1683,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
|
@Nullable
|
|
@Override
|
|
public BlockEntity getBlockEntity(BlockPos pos) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // Leaf - 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) {
|
|
@@ -1692,6 +1700,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
|
|
}
|
|
|
|
public void setBlockEntity(BlockEntity blockEntity) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // Leaf - SparklyPaper - parallel world ticking
|
|
BlockPos blockPos = blockEntity.getBlockPos();
|
|
if (!this.isOutsideBuildHeight(blockPos)) {
|
|
// CraftBukkit start
|
|
@@ -1776,6 +1785,7 @@ 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) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // Leaf - SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
List<Entity> list = Lists.newArrayList();
|
|
|
|
// Paper start - rewrite chunk system
|
|
@@ -2098,8 +2108,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
|
|
+ int i;
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ i = this.random.nextInt() >> 2;
|
|
+ } else {
|
|
+ this.randValue = this.randValue * 3 + 1013904223;
|
|
+ i = this.randValue >> 2;
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
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..38a9d3c23b9ccdfd38e0454181bde762e00e4b3f 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); // Leaf - SparklyPaper - parallel world ticking
|
|
} else if (this == Blocks.CRIMSON_FUNGUS) {
|
|
- SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS;
|
|
+ SaplingBlock.setTreeTypeRT(org.bukkit.TreeType.CRIMSON_FUNGUS); // Leaf - SparklyPaper - parallel world ticking
|
|
}
|
|
return value;
|
|
})
|
|
diff --git a/net/minecraft/world/level/block/MushroomBlock.java b/net/minecraft/world/level/block/MushroomBlock.java
|
|
index d306f5f524dc64618df94c9783c2168dc561a5e3..4d2ecaa6a7e29a1032bb8e8c20845fa106614a00 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 // Leaf - SparklyPaper - parallel world ticking
|
|
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..553b2f664940f01d0d3397179c6197123787dd94 100644
|
|
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
|
|
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
|
|
@@ -66,6 +66,7 @@ public class RedStoneWireBlock extends Block {
|
|
private final BlockState crossState;
|
|
private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this);
|
|
public boolean shouldSignal = true;
|
|
+ private final ThreadLocal<Boolean> shouldSignalTL = ThreadLocal.withInitial(() -> true); // Leaf - SparklyPaper - parallel world ticking
|
|
|
|
@Override
|
|
public MapCodec<RedStoneWireBlock> codec() {
|
|
@@ -283,7 +284,13 @@ 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
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source);
|
|
+ } else {
|
|
+ turbo.updateSurroundingRedstone(worldIn, pos, state, source);
|
|
+ }
|
|
+ // Leaf end - parallel world ticking
|
|
return;
|
|
}
|
|
updatePowerStrength(worldIn, pos, state, orientation, blockAdded);
|
|
@@ -311,7 +318,13 @@ 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
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ level.turbo.updateNeighborShapes(level, pos, state);
|
|
+ } else {
|
|
+ turbo.updateNeighborShapes(level, pos, state);
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
}
|
|
}
|
|
}
|
|
@@ -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
|
|
+ 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
|
|
}
|
|
|
|
private void checkCornerChangeAt(Level level, BlockPos pos) {
|
|
@@ -422,12 +444,18 @@ 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
|
|
+ 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
|
|
}
|
|
|
|
@Override
|
|
protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
|
|
- if (this.shouldSignal && side != Direction.DOWN) {
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ final boolean signal = org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.shouldSignalTL.get() : this.shouldSignal;
|
|
+ if (signal && side != Direction.DOWN) {
|
|
+ // Leaf end - SparklyPaper - parallel world ticking mod (make configurable)
|
|
int powerValue = blockState.getValue(POWER);
|
|
if (powerValue == 0) {
|
|
return 0;
|
|
@@ -459,7 +487,7 @@ public class RedStoneWireBlock extends Block {
|
|
|
|
@Override
|
|
protected boolean isSignalSource(BlockState state) {
|
|
- return this.shouldSignal;
|
|
+ return org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled ? this.shouldSignalTL.get() : this.shouldSignal; // Leaf - SparklyPaper - parallel world ticking
|
|
}
|
|
|
|
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..25b12f90403cf04b66610eb749c0775a012ffdd1 100644
|
|
--- a/net/minecraft/world/level/block/SaplingBlock.java
|
|
+++ b/net/minecraft/world/level/block/SaplingBlock.java
|
|
@@ -26,6 +26,35 @@ 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<>(); // Leaf - SparklyPaper - parallel world ticking (from Folia)
|
|
+
|
|
+ // Leaf start - SparklyPaper - parallel world ticking
|
|
+ // refer to original field in case plugins attempt to modify it
|
|
+ public static org.bukkit.TreeType getTreeTypeRT() {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) {
|
|
+ org.bukkit.TreeType treeTypeRTCopy;
|
|
+ if ((treeTypeRTCopy = treeTypeRT.get()) != null)
|
|
+ return treeTypeRTCopy;
|
|
+ synchronized (SaplingBlock.class) {
|
|
+ return treeType;
|
|
+ }
|
|
+ } else {
|
|
+ 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;
|
|
+ }
|
|
+ } else {
|
|
+ treeType = value;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
|
|
@Override
|
|
public MapCodec<? extends SaplingBlock> codec() {
|
|
@@ -62,14 +91,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(); // Leaf - SparklyPaper - parallel world ticking
|
|
+ SaplingBlock.setTreeTypeRT(null); // Leaf - SparklyPaper - parallel world ticking
|
|
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 - fix missing stuff from original sparkly paper
|
|
+ event = new org.bukkit.event.world.StructureGrowEvent(location, treeTypeLocal, false, null, blocks); // Leaf - SparklyPaper - parallel world ticking - 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 5a094257a31f0500278a706a418e1697f8810ffb..0fac869e152c0895e69b9b8913733be575dae14b 100644
|
|
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
|
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
|
@@ -76,6 +76,12 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
|
|
}
|
|
|
|
public static boolean canUnlock(Player player, LockCode code, Component displayName, @Nullable BlockEntity blockEntity) {
|
|
+ // Leaf start - 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.level()) {
|
|
+ net.minecraft.server.MinecraftServer.LOGGER.warn("Player {} ({}) attempted to open a BlockEntity @ {} {}, {}, {} while they were in a different world {} than the block themselves!", serverPlayer.getScoreboardName(), serverPlayer.getStringUUID(), blockEntity.getLevel().getWorld().getName(), blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getZ(), serverPlayer.level().getWorld().getName());
|
|
+ return false;
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - 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 83c811eb5e493fa6630f16c206787f227fde089b..ae119e98e50d033ba76bb035fa184956dec6caa7 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. // Leaf - SparklyPaper - parallel world ticking
|
|
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 // Leaf - SparklyPaper - parallel world ticking
|
|
}
|
|
|
|
@Override
|
|
diff --git a/net/minecraft/world/level/block/grower/TreeGrower.java b/net/minecraft/world/level/block/grower/TreeGrower.java
|
|
index d23f255de9208f42125fa358a9e8194c984fe4d3..e48fefd6063f5eb1235ef98c65f78d892d1342ca 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - 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; // Leaf - SparklyPaper - parallel world ticking
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown tree generator " + feature);
|
|
}
|
|
+ net.minecraft.world.level.block.SaplingBlock.setTreeTypeRT(treeType); // Leaf - SparklyPaper - parallel world ticking
|
|
}
|
|
// CraftBukkit end
|
|
}
|
|
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
|
index 65442f9ab1528fd1b736963bc51f21fd6a0781a0..24baf9fd222b700a17e064cf61fc3c259d9ae528 100644
|
|
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -401,6 +401,7 @@ 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) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, pos, "Updating block asynchronously"); // Leaf - 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..da533f61b97b4e08cbf069109f484d1b7d2e5e8f 100644
|
|
--- a/net/minecraft/world/level/entity/EntityTickList.java
|
|
+++ b/net/minecraft/world/level/entity/EntityTickList.java
|
|
@@ -11,16 +11,32 @@ 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
|
|
+ // Used to track async entity additions/removals/loops
|
|
+ @Nullable
|
|
+ private final net.minecraft.server.level.ServerLevel serverLevel;
|
|
+
|
|
+ public EntityTickList() {
|
|
+ this(null);
|
|
+ }
|
|
+
|
|
+ public EntityTickList(@Nullable net.minecraft.server.level.ServerLevel serverLevel) {
|
|
+ this.serverLevel = serverLevel;
|
|
+ }
|
|
+ // Leaf end - SparklyPaper - parallel world ticking
|
|
+
|
|
private void ensureActiveIsNotIterated() {
|
|
// Paper - rewrite chunk system
|
|
}
|
|
|
|
public void add(Entity entity) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist addition"); // Paper // Leaf - 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) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist removal"); // Paper // Leaf - SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.ensureActiveIsNotIterated();
|
|
this.entities.remove(entity); // Paper - rewrite chunk system
|
|
}
|
|
@@ -30,6 +46,7 @@ public class EntityTickList {
|
|
}
|
|
|
|
public void forEach(Consumer<Entity> entity) {
|
|
+ if (org.dreeam.leaf.config.modules.async.SparklyPaperParallelWorldTicking.enabled) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverLevel, "Asynchronous entity ticklist iteration"); // Leaf - 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..9c1fa292637f867db1ca2d964f912579e434f750 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) { // Leaf start - SparklyPaper - parallel world ticking
|
|
MapId mapId = new MapId(++this.lastMapId);
|
|
this.setDirty();
|
|
return mapId;
|
|
+ } // Leaf end - SparklyPaper - parallel world ticking
|
|
}
|
|
}
|