mirror of
https://github.com/SparklyPower/SparklyPaper.git
synced 2025-12-19 15:09:27 +00:00
1871 lines
123 KiB
Diff
1871 lines
123 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
|
Date: Tue, 7 Nov 2023 01:34:14 -0300
|
|
Subject: [PATCH] Parallel world ticking
|
|
|
|
"mom can we have folia?" "we already have folia at home" folia at home:
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..a6082e925ad1e27d6a5089356af1840ee8852a9a 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
|
|
@@ -1024,7 +1024,7 @@ public final class ChunkHolderManager {
|
|
if (changedFullStatus.isEmpty()) {
|
|
return;
|
|
}
|
|
- if (!TickThread.isTickThread()) {
|
|
+ if (!TickThread.isTickThreadFor(world)) { // SparklyPaper - parallel world ticking
|
|
this.taskScheduler.scheduleChunkTask(() -> {
|
|
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
|
|
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
|
@@ -1053,7 +1053,7 @@ 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");
|
|
+ TickThread.ensureTickThread(world, "Cannot unload chunks off-main"); // SparklyPaper - parallel world ticking
|
|
|
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
|
throw new IllegalStateException("Cannot unload chunks recursively");
|
|
@@ -1328,14 +1328,14 @@ public final class ChunkHolderManager {
|
|
}
|
|
|
|
private boolean processTicketUpdates(final boolean checkLocks, final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
|
|
- TickThread.ensureTickThread("Cannot process ticket levels off-main");
|
|
+ TickThread.ensureTickThread(world, "Cannot process ticket levels off-main"); // SparklyPaper - parallel world ticking
|
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
|
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
|
|
}
|
|
|
|
List<NewChunkHolder> changedFullStatus = null;
|
|
|
|
- final boolean isTickThread = TickThread.isTickThread();
|
|
+ final boolean isTickThread = TickThread.isTickThreadFor(world);
|
|
|
|
boolean ret = false;
|
|
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
|
index 17ce14f2dcbf900890efbc2351782bc6f8867068..3d762963d9b7bbb355358ccdb453a32c3651bc51 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
|
|
@@ -330,7 +330,7 @@ public final class ChunkTaskScheduler {
|
|
public void scheduleTickingState(final int chunkX, final int chunkZ, final FullChunkStatus toStatus,
|
|
final boolean addTicket, final PrioritisedExecutor.Priority priority,
|
|
final Consumer<LevelChunk> onComplete) {
|
|
- if (!TickThread.isTickThread()) {
|
|
+ if (!TickThread.isTickThreadFor(world, chunkX, chunkZ)) { // SparklyPaper - parallel world ticking (other threads may call this method, such as the container stillValid check, which may trigger a chunk load in a different thread)
|
|
this.scheduleChunkTask(chunkX, chunkZ, () -> {
|
|
ChunkTaskScheduler.this.scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
|
}, priority);
|
|
@@ -486,7 +486,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
public void scheduleChunkLoad(final int chunkX, final int chunkZ, final ChunkStatus toStatus, final boolean addTicket,
|
|
final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
|
|
- if (!TickThread.isTickThread()) {
|
|
+ if (!TickThread.isTickThreadFor(world, chunkX, chunkZ)) { // SparklyPaper - parallel world ticking
|
|
this.scheduleChunkTask(chunkX, chunkZ, () -> {
|
|
ChunkTaskScheduler.this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
|
}, priority);
|
|
diff --git a/src/main/java/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java
|
|
index bdaf062f9b66ceab303a0807eca301342886a8ea..205034bf47e5be6bd151bb538408d76a91633273 100644
|
|
--- a/src/main/java/io/papermc/paper/util/TickThread.java
|
|
+++ b/src/main/java/io/papermc/paper/util/TickThread.java
|
|
@@ -17,6 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|
public class TickThread extends Thread {
|
|
|
|
public static final boolean STRICT_THREAD_CHECKS = Boolean.getBoolean("paper.strict-thread-checks");
|
|
+ public static final boolean HARD_THROW = !Boolean.getBoolean("sparklypaper.disableHardThrow"); // SparklyPaper - parallel world ticking - THIS SHOULD NOT BE DISABLED SINCE IT CAN CAUSE DATA CORRUPTION!!! Anyhow, for production servers, if you want to make a test run to see if the server could crash, you can test it with this disabled
|
|
|
|
static {
|
|
if (STRICT_THREAD_CHECKS) {
|
|
@@ -41,50 +42,93 @@ public class TickThread extends Thread {
|
|
@Deprecated
|
|
public static void ensureTickThread(final String reason) {
|
|
if (!isTickThread()) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final ServerLevel world, final BlockPos pos, final String reason) {
|
|
if (!isTickThreadFor(world, pos)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " blockPos: " + pos + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final ServerLevel world, final ChunkPos pos, final String reason) {
|
|
if (!isTickThreadFor(world, pos)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " chunkPos: " + pos + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final ServerLevel world, final int chunkX, final int chunkZ, final String reason) {
|
|
if (!isTickThreadFor(world, chunkX, chunkZ)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " chunkX: " + chunkX + " chunkZ: " + chunkZ + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final Entity entity, final String reason) {
|
|
if (!isTickThreadFor(entity)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ entity " + entity.getStringUUID() + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final ServerLevel world, final AABB aabb, final String reason) {
|
|
if (!isTickThreadFor(world, aabb)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " aabb: " + aabb + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final ServerLevel world, final double blockX, final double blockZ, final String reason) {
|
|
if (!isTickThreadFor(world, blockX, blockZ)) {
|
|
- MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " blockX: " + blockX + " blockZ: " + blockZ + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // SparklyPaper - parallel world ticking
|
|
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
|
|
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
|
|
+ public static void ensureTickThread(final ServerLevel world, final String reason) {
|
|
+ if (!isTickThreadFor(world)) {
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // SparklyPaper - parallel world ticking
|
|
+ // This is an additional method to check if it is a tick thread but ONLY a tick thread
|
|
+ public static void ensureOnlyTickThread(final String reason) {
|
|
+ boolean isTickThread = isTickThread();
|
|
+ boolean isServerLevelTickThread = isServerLevelTickThread();
|
|
+ if (!isTickThread || isServerLevelTickThread) {
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread ONLY tick thread check: " + reason + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // SparklyPaper - parallel world ticking
|
|
+ // This is an additional method to check if the tick thread is bound to a specific world or if it is an async thread.
|
|
+ public static void ensureTickThreadOrAsyncThread(final ServerLevel world, final String reason) {
|
|
+ boolean isValidTickThread = isTickThreadFor(world);
|
|
+ boolean isAsyncThread = !isTickThread();
|
|
+ boolean isValid = isAsyncThread || isValidTickThread;
|
|
+ if (!isValid) {
|
|
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread or async thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(), new Throwable());
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
@@ -109,6 +153,32 @@ public class TickThread extends Thread {
|
|
return (TickThread) Thread.currentThread();
|
|
}
|
|
|
|
+ public static String getTickThreadInformation() {
|
|
+ StringBuilder sb = new StringBuilder();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+ sb.append("Is tick thread? ");
|
|
+ sb.append(currentThread instanceof TickThread);
|
|
+ sb.append("; Is server level tick thread? ");
|
|
+ sb.append(currentThread instanceof ServerLevelTickThread);
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ sb.append("; Currently ticking level: ");
|
|
+ if (serverLevelTickThread.currentlyTickingServerLevel != null) {
|
|
+ sb.append(serverLevelTickThread.currentlyTickingServerLevel.getWorld().getName());
|
|
+ } else {
|
|
+ sb.append("null");
|
|
+ }
|
|
+ }
|
|
+ sb.append("; Is iterating over levels? ");
|
|
+ sb.append(MinecraftServer.getServer().isIteratingOverLevels);
|
|
+ sb.append("; Are we going to hard throw? ");
|
|
+ sb.append(HARD_THROW);
|
|
+ return sb.toString();
|
|
+ }
|
|
+
|
|
+ public static boolean isServerLevelTickThread() {
|
|
+ return Thread.currentThread() instanceof ServerLevelTickThread;
|
|
+ }
|
|
+
|
|
public static boolean isTickThread() {
|
|
return Thread.currentThread() instanceof TickThread;
|
|
}
|
|
@@ -118,42 +188,111 @@ public class TickThread extends Thread {
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final BlockPos pos) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final ChunkPos pos) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final Vec3 pos) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final AABB aabb) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final double blockX, final double blockZ) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final Vec3 position, final Vec3 deltaMovement, final int buffer) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final int fromChunkX, final int fromChunkZ, final int toChunkX, final int toChunkZ) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ, final int radius) {
|
|
- return isTickThread();
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
+ }
|
|
+
|
|
+ // SparklyPaper - parallel world ticking
|
|
+ // This is an additional method to check if the tick thread is bound to a specific world because, by default, Paper's isTickThread methods do not provide this information
|
|
+ // Because we only tick worlds in parallel (instead of regions), we can use this for our checks
|
|
+ public static boolean isTickThreadFor(final ServerLevel world) {
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
|
|
+ } else return currentThread instanceof TickThread;
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final Entity entity) {
|
|
- return isTickThread();
|
|
+ if (entity == null) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ Thread currentThread = Thread.currentThread();
|
|
+
|
|
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
|
|
+ return serverLevelTickThread.currentlyTickingServerLevel == entity.level();
|
|
+ } else return currentThread instanceof TickThread;
|
|
+ }
|
|
+
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ public static class ServerLevelTickThread extends TickThread {
|
|
+ public ServerLevelTickThread(String name) {
|
|
+ super(name);
|
|
+ }
|
|
+
|
|
+ public ServerLevelTickThread(Runnable run, String name) {
|
|
+ super(run, name);
|
|
+ }
|
|
+
|
|
+ public ServerLevel currentlyTickingServerLevel;
|
|
}
|
|
+ // SparklyPaper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/AbstractProjectileDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/AbstractProjectileDispenseBehavior.java
|
|
index bc2e763a848b4bf7e9598ffe1ca2aa35a9af4677..fbe23ee367f2cb58c9ba9ba5bb1d444880b3a8e5 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/AbstractProjectileDispenseBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/AbstractProjectileDispenseBehavior.java
|
|
@@ -32,7 +32,7 @@ public abstract class AbstractProjectileDispenseBehavior extends DefaultDispense
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumdirection.getStepX(), (double) ((float) enumdirection.getStepY() + 0.1F), (double) enumdirection.getStepZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
|
|
index 6df0db8b4cdab23494ea34236949ece4989110a3..62943b954835fb30ce916c1a17fed39620a7882d 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
|
|
@@ -63,7 +63,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
|
|
index f28705547a62da790f5df071400986aacba39367..f9800dce8f9e85ad11e4208d78d85a75dbcd1f72 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
|
|
@@ -85,7 +85,7 @@ public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), CraftVector.toBukkit(entityitem.getDeltaMovement()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
index e2e1273d787536d2fe1bdbbf8af36eb5ac220599..2daeee929592c64a9a12a2b2fe34f516399884c1 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
|
@@ -222,7 +222,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -281,7 +281,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -338,7 +338,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -394,7 +394,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityhorseabstract.getBukkitEntity());
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -468,7 +468,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityhorsechestedabstract.getBukkitEntity());
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -507,7 +507,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(enumdirection.getStepX(), enumdirection.getStepY(), enumdirection.getStepZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -565,7 +565,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -645,7 +645,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -722,7 +722,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -769,7 +769,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -830,7 +830,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -859,8 +859,8 @@ public interface DispenseItemBehavior {
|
|
// CraftBukkit start
|
|
worldserver.captureTreeGeneration = false;
|
|
if (worldserver.capturedBlockStates.size() > 0) {
|
|
- TreeType treeType = SaplingBlock.treeType;
|
|
- SaplingBlock.treeType = null;
|
|
+ TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
|
|
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
|
|
Location location = CraftLocation.toBukkit(blockposition, worldserver.getWorld());
|
|
List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(worldserver.capturedBlockStates.values());
|
|
worldserver.capturedBlockStates.clear();
|
|
@@ -899,7 +899,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -956,7 +956,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -1005,7 +1005,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
@@ -1078,7 +1078,7 @@ public interface DispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - only single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
index 8d65cdb013706a932c2c73231108b2810b99e1c7..77741616d3b208c55d84a2246dbaf6cc16180187 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
|
|
@@ -40,7 +40,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
|
|
index cb308808906a8cdb127df8284e106e00553473ca..323d41e2bed5e83a26dfe4c88dfce7ed87cdfb4c 100644
|
|
--- a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
|
|
+++ b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
|
|
@@ -37,7 +37,7 @@ public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
pointer.level().getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 3a1802c5b759604782fd7fcdf434a4c310c4455c..98a0d108c8b64759951b0f428b645642c9542c27 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -314,6 +314,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
|
|
public final Set<Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
|
|
public net.sparklypower.sparklypaper.HalloweenManager halloweenManager = new net.sparklypower.sparklypaper.HalloweenManager(); // SparklyPaper - Spooky month optimizations
|
|
+ // SparklyPaper - parallel world ticking
|
|
+ public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null;
|
|
+ // SparklyPaper end
|
|
|
|
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
|
|
AtomicReference<S> atomicreference = new AtomicReference();
|
|
@@ -1715,55 +1718,116 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
|
|
Iterator iterator = this.getAllLevels().iterator(); // Paper - Throw exception on world create while being ticked; move down
|
|
- while (iterator.hasNext()) {
|
|
- ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
- worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
|
- worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
|
- net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
|
|
- worldserver.updateLagCompensationTick(); // Paper - lag compensation
|
|
-
|
|
- this.profiler.push(() -> {
|
|
- return worldserver + " " + worldserver.dimension().location();
|
|
- });
|
|
- /* Drop global time updates
|
|
- if (this.tickCount % 20 == 0) {
|
|
- this.profiler.push("timeSync");
|
|
- this.synchronizeTime(worldserver);
|
|
- this.profiler.pop();
|
|
- }
|
|
- // CraftBukkit end */
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ java.util.ArrayDeque<java.util.concurrent.Future<ServerLevel>> tasks = new java.util.ArrayDeque<>();
|
|
+ // while (iterator.hasNext()) {
|
|
+ // ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
+ // worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
|
+ // worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
|
+ // net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
|
|
+ // worldserver.updateLagCompensationTick(); // Paper - lag compensation
|
|
+ //
|
|
+ // this.profiler.push(() -> {
|
|
+ // return worldserver + " " + worldserver.dimension().location();
|
|
+ // });
|
|
+ // /* Drop global time updates
|
|
+ // if (this.tickCount % 20 == 0) {
|
|
+ // this.profiler.push("timeSync");
|
|
+ // this.synchronizeTime(worldserver);
|
|
+ // this.profiler.pop();
|
|
+ // }
|
|
+ // // CraftBukkit end */
|
|
+ //
|
|
+ // this.profiler.push("tick");
|
|
+ //
|
|
+ // try {
|
|
+ // worldserver.timings.doTick.startTiming(); // Spigot
|
|
+ // long i = Util.getNanos(); // SparklyPaper - track world's MSPT
|
|
+ // worldserver.tick(shouldKeepTicking);
|
|
+ // // SparklyPaper start - track world's MSPT
|
|
+ // long j = Util.getNanos() - i;
|
|
+ //
|
|
+ // // These are from the "tickServer" function
|
|
+ // worldserver.tickTimes5s.add(this.tickCount, j);
|
|
+ // worldserver.tickTimes10s.add(this.tickCount, j);
|
|
+ // worldserver.tickTimes60s.add(this.tickCount, j);
|
|
+ // // SparklyPaper end
|
|
+ // // Paper start
|
|
+ // for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
|
|
+ // regionManager.recalculateRegions();
|
|
+ // }
|
|
+ // // Paper end
|
|
+ // worldserver.timings.doTick.stopTiming(); // Spigot
|
|
+ // } catch (Throwable throwable) {
|
|
+ // CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
|
|
+ //
|
|
+ // worldserver.fillReportDetails(crashreport);
|
|
+ // throw new ReportedException(crashreport);
|
|
+ // }
|
|
+ //
|
|
+ // this.profiler.pop();
|
|
+ // this.profiler.pop();
|
|
+ // worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
+ // }
|
|
+ try {
|
|
+ while (iterator.hasNext()) {
|
|
+ ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
+ worldserver.updateLagCompensationTick(); // Paper - lag compensation
|
|
+ worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
|
|
+ worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
+
|
|
+ serverLevelTickingSemaphore.acquire();
|
|
+ tasks.add(
|
|
+ worldserver.tickExecutor.submit(() -> {
|
|
+ try {
|
|
+ io.papermc.paper.util.TickThread.ServerLevelTickThread currentThread = (io.papermc.paper.util.TickThread.ServerLevelTickThread) Thread.currentThread();
|
|
+ currentThread.currentlyTickingServerLevel = worldserver;
|
|
+
|
|
+ long i = Util.getNanos(); // SparklyPaper - track world's MSPT
|
|
+ worldserver.tick(shouldKeepTicking);
|
|
+ for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
|
|
+ regionManager.recalculateRegions();
|
|
+ }
|
|
+ worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
|
|
- this.profiler.push("tick");
|
|
+ // SparklyPaper start - track world's MSPT
|
|
+ long j = Util.getNanos() - i;
|
|
|
|
- try {
|
|
- worldserver.timings.doTick.startTiming(); // Spigot
|
|
- long i = Util.getNanos(); // SparklyPaper - track world's MSPT
|
|
- worldserver.tick(shouldKeepTicking);
|
|
- // SparklyPaper start - track world's MSPT
|
|
- long j = Util.getNanos() - i;
|
|
-
|
|
- // These are from the "tickServer" function
|
|
- worldserver.tickTimes5s.add(this.tickCount, j);
|
|
- worldserver.tickTimes10s.add(this.tickCount, j);
|
|
- worldserver.tickTimes60s.add(this.tickCount, j);
|
|
- // SparklyPaper end
|
|
- // Paper start
|
|
- for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
|
|
- regionManager.recalculateRegions();
|
|
- }
|
|
- // Paper end
|
|
- worldserver.timings.doTick.stopTiming(); // Spigot
|
|
- } catch (Throwable throwable) {
|
|
- CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
|
|
+ // These are from the "tickServer" function
|
|
+ worldserver.tickTimes5s.add(this.tickCount, j);
|
|
+ worldserver.tickTimes10s.add(this.tickCount, j);
|
|
+ worldserver.tickTimes60s.add(this.tickCount, j);
|
|
+ // SparklyPaper end
|
|
|
|
- worldserver.fillReportDetails(crashreport);
|
|
- throw new ReportedException(crashreport);
|
|
+ currentThread.currentlyTickingServerLevel = null; // Reset current ticking level
|
|
+ } catch (Throwable throwable) {
|
|
+ // Spigot Start
|
|
+ CrashReport crashreport;
|
|
+ try {
|
|
+ crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
|
|
+ } catch (Throwable t) {
|
|
+ if (throwable instanceof ThreadDeath) {
|
|
+ throw (ThreadDeath) throwable;
|
|
+ } // Paper
|
|
+ throw new RuntimeException("Error generating crash report", t);
|
|
+ }
|
|
+ // Spigot End
|
|
+ worldserver.fillReportDetails(crashreport);
|
|
+ throw new ReportedException(crashreport);
|
|
+ } finally {
|
|
+ serverLevelTickingSemaphore.release();
|
|
+ }
|
|
+ }, worldserver)
|
|
+ );
|
|
}
|
|
-
|
|
- this.profiler.pop();
|
|
- this.profiler.pop();
|
|
- worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
+ while (!tasks.isEmpty()) {
|
|
+ tasks.pop().get();
|
|
+ }
|
|
+ } catch (java.lang.InterruptedException | java.util.concurrent.ExecutionException e) {
|
|
+ throw new RuntimeException(e); // Propagate exception
|
|
}
|
|
+ // SparklyPaper end
|
|
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
|
|
|
this.profiler.popPush("connection");
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index 4c549a2656183e4e4bbaf3f7d5169f3d258e81ce..dd889a41412ce62bd362d82c44dea71eb490a05c 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
@@ -17,6 +17,7 @@ import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Optional;
|
|
+import java.util.concurrent.TimeUnit;
|
|
import java.util.function.BooleanSupplier;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.DefaultUncaughtExceptionHandler;
|
|
@@ -50,6 +51,7 @@ import net.minecraft.world.level.GameRules;
|
|
import net.minecraft.world.level.GameType;
|
|
import net.minecraft.world.level.block.entity.SkullBlockEntity;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
+import net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils;
|
|
import org.slf4j.Logger;
|
|
|
|
// CraftBukkit start
|
|
@@ -227,6 +229,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
return false;
|
|
}
|
|
net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this);
|
|
+ serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getConfig().getParallelWorldTicking().getThreads()); // SparklyPaper - parallel world ticking
|
|
+ DedicatedServer.LOGGER.info("Using " + serverLevelTickingSemaphore.availablePermits() + " permits for parallel world ticking"); // SparklyPaper - parallel world ticking
|
|
// SparklyPaper end
|
|
// SparklyPaper start - Spooky month optimizations
|
|
halloweenManager.startHalloweenEpochTask();
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 53bce70f5cc14672d41618747d3919429896001f..ec27e20d661ecde3992a4b4366cf19b2cb1ee2d5 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -197,7 +197,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
public LevelChunk getChunkAtIfLoadedImmediately(int x, int z) {
|
|
long k = ChunkPos.asLong(x, z);
|
|
|
|
- if (io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
|
|
+ if (io.papermc.paper.util.TickThread.isTickThreadFor(level, x, z)) { // Paper - rewrite chunk system // SparklyPaper - parallel world ticking
|
|
return this.getChunkAtIfLoadedMainThread(x, z);
|
|
}
|
|
|
|
@@ -250,7 +250,16 @@ public class ServerChunkCache extends ChunkSource {
|
|
@Override
|
|
public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
|
|
final int x1 = x; final int z1 = z; // Paper - conflict on variable change
|
|
- if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
|
|
+ if (!io.papermc.paper.util.TickThread.isTickThreadFor(level, x, z)) { // Paper - rewrite chunk system // SparklyPaper - parallel world ticking
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (io.papermc.paper.util.TickThread.isServerLevelTickThread()) {
|
|
+ // Welp, we are going to crash, bye
|
|
+ net.minecraft.server.MinecraftServer.LOGGER.error("THE SERVER IS GOING TO CRASH! - Thread " + Thread.currentThread().getName() + " failed main thread check: Cannot query another world's (" + level.getWorld().getName() + ") chunk (" + x + ", " + z + ") in a ServerLevelTickThread - " + io.papermc.paper.util.TickThread.getTickThreadInformation(), new Throwable());
|
|
+ if (io.papermc.paper.util.TickThread.HARD_THROW)
|
|
+ throw new IllegalStateException("Cannot query another world's (" + level.getWorld().getName() + ") chunk (" + x + ", " + z + ") in a ServerLevelTickThread");
|
|
+ }
|
|
+ // SparklyPaper end
|
|
+
|
|
return (ChunkAccess) CompletableFuture.supplyAsync(() -> {
|
|
return this.getChunk(x, z, leastStatus, create);
|
|
}, this.mainThreadProcessor).join();
|
|
@@ -300,7 +309,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
@Nullable
|
|
@Override
|
|
public LevelChunk getChunkNow(int chunkX, int chunkZ) {
|
|
- if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
|
|
+ if (!io.papermc.paper.util.TickThread.isTickThreadFor(level, chunkX, chunkZ)) { // Paper - rewrite chunk system // SparklyPaper - parallel world ticking
|
|
return null;
|
|
} else {
|
|
return this.getChunkAtIfLoadedMainThread(chunkX, chunkZ); // Paper - Perf: Optimise getChunkAt calls for loaded chunks
|
|
@@ -314,7 +323,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
}
|
|
|
|
public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFuture(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
|
|
- boolean flag1 = io.papermc.paper.util.TickThread.isTickThread(); // Paper - rewrite chunk system
|
|
+ boolean flag1 = io.papermc.paper.util.TickThread.isTickThreadFor(level, chunkX, chunkZ); // Paper - rewrite chunk system // SparklyPaper - parallel world ticking
|
|
CompletableFuture completablefuture;
|
|
|
|
if (flag1) {
|
|
@@ -638,7 +647,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
|
|
if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - optimise chunk tick iteration
|
|
this.level.tickChunk(chunk1, l);
|
|
- if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
|
|
+ // if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Paper
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index 439fabd23e8cb968879fdb6959aff6d70b28ec0e..53175d8584e19b1caebd614e0c61356cd227f29d 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -217,6 +217,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
private final boolean tickTime;
|
|
private final RandomSequences randomSequences;
|
|
public long lastMidTickExecuteFailure; // Paper - execute chunk tasks mid tick
|
|
+ public java.util.concurrent.ExecutorService tickExecutor; // SparklyPaper - parallel world ticking
|
|
|
|
// CraftBukkit start
|
|
public final LevelStorageSource.LevelStorageAccess convertable;
|
|
@@ -704,7 +705,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile());
|
|
// CraftBukkit end
|
|
this.players = Lists.newArrayList();
|
|
- this.entityTickList = new EntityTickList();
|
|
+ this.entityTickList = new EntityTickList(this);
|
|
this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
|
|
this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier());
|
|
this.navigatingMobs = new ObjectOpenHashSet();
|
|
@@ -775,6 +776,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
this.chunkTaskScheduler = new io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler(this, io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.workerThreads); // Paper - rewrite chunk system
|
|
this.entityLookup = new io.papermc.paper.chunk.system.entity.EntityLookup(this, new EntityCallbacks()); // Paper - rewrite chunk system
|
|
+ this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new net.sparklypower.sparklypaper.ServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking
|
|
}
|
|
|
|
// Paper start
|
|
@@ -1352,7 +1354,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (fluid1.is(fluid)) {
|
|
fluid1.tick(this, pos);
|
|
}
|
|
- MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
|
+ // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Paper - exec chunk tasks during world tick
|
|
|
|
}
|
|
|
|
@@ -1362,7 +1364,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
if (iblockdata.is(block)) {
|
|
iblockdata.tick(this, pos, this.random);
|
|
}
|
|
- MinecraftServer.getServer().executeMidTickTasks(); // Paper - exec chunk tasks during world tick
|
|
+ // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Paper - exec chunk tasks during world tick
|
|
|
|
}
|
|
|
|
@@ -1380,7 +1382,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
public void tickNonPassenger(Entity entity) {
|
|
// Paper start - log detailed entity tick information
|
|
- io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(entity, "Cannot tick an entity off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
try {
|
|
if (currentlyTickingEntity.get() == null) {
|
|
currentlyTickingEntity.lazySet(entity);
|
|
@@ -1668,6 +1670,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
|
|
private void addPlayer(ServerPlayer player) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
Entity entity = (Entity) this.getEntities().get(player.getUUID());
|
|
|
|
if (entity != null) {
|
|
@@ -1681,7 +1684,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
|
|
// CraftBukkit start
|
|
private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot add entity off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
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) {
|
|
@@ -2652,6 +2655,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
@Override
|
|
public void close() throws IOException {
|
|
super.close();
|
|
+ tickExecutor.shutdown(); // SparklyPaper - parallel world ticking
|
|
//this.entityManager.close(); // Paper - rewrite chunk system
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
index 58591bf2f63b9c5e97d9ce4188dff3366968a178..3debae03ef3d6e01699be2f49a43835f7ed83608 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -328,6 +328,7 @@ public class ServerPlayer extends Player {
|
|
// Paper start - optimise chunk tick iteration
|
|
public double lastEntitySpawnRadiusSquared = -1.0;
|
|
// Paper end - optimise chunk tick iteration
|
|
+ public boolean hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (fixes bug in DreamResourceReset where the inventory is opened AFTER the player has changed worlds, if you click with the quick tp torch in a chest, because the inventory is opened AFTER the player has teleported)
|
|
|
|
public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) {
|
|
super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile);
|
|
@@ -717,6 +718,7 @@ public class ServerPlayer extends Player {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ hasTickedAtLeastOnceInNewWorld = true; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
// CraftBukkit start
|
|
if (this.joining) {
|
|
this.joining = false;
|
|
@@ -1185,6 +1187,7 @@ public class ServerPlayer extends Player {
|
|
ResourceKey<LevelStem> resourcekey = worldserver1.getTypeKey(); // CraftBukkit
|
|
|
|
if (resourcekey == LevelStem.END && worldserver != null && worldserver.getTypeKey() == LevelStem.OVERWORLD) { // CraftBukkit
|
|
+ io.papermc.paper.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel().getWorld().getName() + " to world " + worldserver.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.isChangingDimension = true; // CraftBukkit - Moved down from above
|
|
this.unRide();
|
|
this.serverLevel().removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
|
|
@@ -1197,6 +1200,10 @@ public class ServerPlayer extends Player {
|
|
|
|
return this;
|
|
} else {
|
|
+ if (worldserver != null)
|
|
+ io.papermc.paper.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel().getWorld().getName() + " to world " + worldserver.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
+ else
|
|
+ io.papermc.paper.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel().getWorld().getName() + " to unknown world"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// CraftBukkit start
|
|
/*
|
|
WorldData worlddata = worldserver.getLevelData();
|
|
@@ -1605,6 +1612,12 @@ public class ServerPlayer extends Player {
|
|
return OptionalInt.empty();
|
|
} else {
|
|
// CraftBukkit start
|
|
+ // SparklyPaper start - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
+ if (!hasTickedAtLeastOnceInNewWorld) {
|
|
+ MinecraftServer.LOGGER.warn("Ignoring request to open container " + container + " because we haven't ticked in the current world yet!", new Throwable());
|
|
+ return OptionalInt.empty();
|
|
+ }
|
|
+ // SparklyPaper end
|
|
this.containerMenu = container;
|
|
if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), Objects.requireNonNullElseGet(title, container::getTitle))); // Paper - Add titleOverride to InventoryOpenEvent
|
|
// CraftBukkit end
|
|
@@ -1666,6 +1679,11 @@ public class ServerPlayer extends Player {
|
|
}
|
|
@Override
|
|
public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
|
|
+ // SparklyPaper start - parallel world ticking (debugging)
|
|
+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) {
|
|
+ MinecraftServer.LOGGER.warn("Closing " + this.getBukkitEntity().getName() + " inventory that was created at", this.containerMenu.containerCreationStacktrace);
|
|
+ }
|
|
+ // SparklyPaper end
|
|
CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
|
|
// Paper end - Inventory close reason
|
|
this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 1e5f709115007ff19901c0a6c3cf884d9e4d3a6c..5d1b1da365a8a932094dfc3419019df6fd25ed5b 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -135,7 +135,7 @@ public abstract class PlayerList {
|
|
private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
|
|
private final MinecraftServer server;
|
|
public final List<ServerPlayer> players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety
|
|
- private final Map<UUID, ServerPlayer> playersByUUID = Maps.newHashMap();
|
|
+ private final Map<UUID, ServerPlayer> playersByUUID = Maps.newHashMap(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map)
|
|
private final UserBanList bans;
|
|
private final IpBanList ipBans;
|
|
private final ServerOpList ops;
|
|
@@ -156,7 +156,7 @@ public abstract class PlayerList {
|
|
|
|
// CraftBukkit start
|
|
private CraftServer cserver;
|
|
- private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>();
|
|
+ private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map)
|
|
public @Nullable String collideRuleTeamName; // Paper - Configurable player collision
|
|
|
|
public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registryManager, PlayerDataStorage saveHandler, int maxPlayers) {
|
|
@@ -180,6 +180,7 @@ public abstract class PlayerList {
|
|
abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor
|
|
|
|
public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie clientData) {
|
|
+ io.papermc.paper.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
player.isRealPlayer = true; // Paper
|
|
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
|
|
GameProfile gameprofile = player.getGameProfile();
|
|
@@ -818,6 +819,8 @@ public abstract class PlayerList {
|
|
}
|
|
|
|
public ServerPlayer respawn(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation, RespawnReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag...respawnFlags) {
|
|
+ System.out.println("respawning player - current player container is " + entityplayer.containerMenu + " but their inventory is " + entityplayer.inventoryMenu);
|
|
+ io.papermc.paper.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + entityplayer.serverLevel().getWorld().getName() + " to world " + worldserver.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// Paper end - Expand PlayerRespawnEvent
|
|
entityplayer.stopRiding(); // CraftBukkit
|
|
this.players.remove(entityplayer);
|
|
@@ -842,6 +845,7 @@ public abstract class PlayerList {
|
|
ServerPlayer entityplayer1 = entityplayer;
|
|
org.bukkit.World fromWorld = entityplayer.getBukkitEntity().getWorld();
|
|
entityplayer.wonGame = false;
|
|
+ entityplayer.hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch)
|
|
// CraftBukkit end
|
|
|
|
entityplayer1.connection = entityplayer.connection;
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index 023d1dccc23efbb862c923b925b798ac415d0401..0c7939d709f75e8821eb422fa727740c77add740 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -826,7 +826,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
|
|
// CraftBukkit start
|
|
public void postTick() {
|
|
// No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
|
|
- if (!(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities
|
|
+ if (false && !(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities // SparklyPaper - parallel world ticking (see issue #9, this is executed in the server level tick for non-player entities)
|
|
this.handleNetherPortal();
|
|
}
|
|
}
|
|
@@ -3976,6 +3976,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
|
|
this.teleportPassengers();
|
|
this.setYHeadRot(yaw);
|
|
} else {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(world, "Cannot teleport entity to another world off-main, from world " + level.getWorld().getName() + " to world " + world.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.unRide();
|
|
Entity entity = this.getType().create(world);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
index 48f634a7521d31c1e9dfd3cfc83139d428dbd37a..072dfc7355f0fade0b007c800fb633dd4739adb5 100644
|
|
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
|
|
@@ -102,8 +102,14 @@ public abstract class AbstractContainerMenu {
|
|
this.title = title;
|
|
}
|
|
// CraftBukkit end
|
|
+ public Throwable containerCreationStacktrace; // SparklyPaper - parallel world ticking (debugging)
|
|
|
|
protected AbstractContainerMenu(@Nullable MenuType<?> type, int syncId) {
|
|
+ // SparklyPaper - parallel world ticking (debugging)
|
|
+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) {
|
|
+ this.containerCreationStacktrace = new Throwable();
|
|
+ }
|
|
+ // SparklyPaper end
|
|
this.carried = ItemStack.EMPTY;
|
|
this.remoteSlots = NonNullList.create();
|
|
this.remoteDataSlots = new IntArrayList();
|
|
diff --git a/src/main/java/net/minecraft/world/item/ArmorItem.java b/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
index 6b81be03f87967124b046708557e05d519aa79e4..f9a4e71f1bdd59256bceb7af92a53e435feea55e 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ArmorItem.java
|
|
@@ -75,7 +75,7 @@ public class ArmorItem extends Item implements Equipable {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityliving.getBukkitEntity());
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
world.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
index 1ad126d992d95062a3db08374db7a927f23a0cac..a75fa6301d597b5f295ea8ba82ce011dda159d16 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -400,8 +400,8 @@ public final class ItemStack {
|
|
if (enuminteractionresult.consumesAction() && world.captureTreeGeneration && world.capturedBlockStates.size() > 0) {
|
|
world.captureTreeGeneration = false;
|
|
Location location = CraftLocation.toBukkit(blockposition, world.getWorld());
|
|
- TreeType treeType = SaplingBlock.treeType;
|
|
- SaplingBlock.treeType = null;
|
|
+ TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
|
|
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
|
|
List<CraftBlockState> blocks = new java.util.ArrayList<>(world.capturedBlockStates.values());
|
|
world.capturedBlockStates.clear();
|
|
StructureGrowEvent structureEvent = null;
|
|
diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
index 3aa73cd44aa8c86b78c35bc1788e4f83018c49ed..eac9b4148d0951f7c54ce4ad8ab4d97eb5f74578 100644
|
|
--- a/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java
|
|
@@ -70,7 +70,7 @@ public class MinecartItem extends Item {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
|
|
|
BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2));
|
|
- if (!DispenserBlock.eventFired) {
|
|
+ if (!DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
worldserver.getCraftServer().getPluginManager().callEvent(event);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 89de470a672cce6f67c454a608d539aace2f2b7f..bc78207d41a61fdfa20753de5478236a5932193e 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -10,6 +10,8 @@ import java.util.function.Consumer;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import javax.annotation.Nullable;
|
|
+
|
|
+import io.papermc.paper.util.TickThread;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportedException;
|
|
@@ -174,6 +176,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
|
|
public net.sparklypower.sparklypaper.configs.SparklyPaperWorldConfig sparklyPaperConfig; // SparklyPaper
|
|
+ public com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE); // SparklyPaper - parallel world ticking (moved to world)
|
|
public final co.aikar.timings.WorldTimingsHandler timings; // Paper
|
|
public static BlockPos lastPhysicsProblem; // Spigot
|
|
private org.spigotmc.TickLimiter entityLimiter;
|
|
@@ -829,7 +832,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
|
|
// Paper start - Perf: make sure loaded chunks get the inlined variant of this function
|
|
net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
|
|
- if (cps.mainThread == Thread.currentThread()) {
|
|
+ if (TickThread.isTickThreadFor((ServerLevel) this, chunkX, chunkZ)) { // SparklyPaper - parallel world ticking, let tick threads load chunks via the main thread
|
|
LevelChunk ifLoaded = cps.getChunkAtIfLoadedMainThread(chunkX, chunkZ);
|
|
if (ifLoaded != null) {
|
|
return ifLoaded;
|
|
@@ -913,6 +916,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
@Override
|
|
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// CraftBukkit start - tree generation
|
|
if (this.captureTreeGeneration) {
|
|
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
|
|
@@ -1314,7 +1318,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
tickingblockentity.tick();
|
|
// Paper start - execute chunk tasks during tick
|
|
if ((this.tileTickPosition & 7) == 0) {
|
|
- MinecraftServer.getServer().executeMidTickTasks();
|
|
+ // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks)
|
|
}
|
|
// Paper end - execute chunk tasks during tick
|
|
} // SparklyPaper end
|
|
@@ -1331,7 +1335,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
|
|
try {
|
|
tickConsumer.accept(entity);
|
|
- MinecraftServer.getServer().executeMidTickTasks(); // Paper - execute chunk tasks mid tick
|
|
+ // MinecraftServer.getServer().executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) // Paper - execute chunk tasks mid tick
|
|
} catch (Throwable throwable) {
|
|
if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
|
// Paper start - Prevent block entity and entity crashes
|
|
@@ -1438,6 +1442,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
@Nullable
|
|
public BlockEntity getBlockEntity(BlockPos blockposition, boolean validate) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // SparklyPaper start - 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(blockposition)) != null) {
|
|
@@ -1445,10 +1450,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
// Paper end - Perf: Optimize capturedTileEntities lookup
|
|
// CraftBukkit end
|
|
- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
|
|
+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system // SparklyPaper - parallel world ticking
|
|
}
|
|
|
|
public void setBlockEntity(BlockEntity blockEntity) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // SparklyPaper start - parallel world ticking
|
|
BlockPos blockposition = blockEntity.getBlockPos();
|
|
|
|
if (!this.isOutsideBuildHeight(blockposition)) {
|
|
@@ -1534,6 +1540,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
|
|
@Override
|
|
public List<Entity> getEntities(@Nullable Entity except, AABB box, Predicate<? super Entity> predicate) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread((ServerLevel)this, box, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.getProfiler().incrementCounter("getEntities");
|
|
List<Entity> list = Lists.newArrayList();
|
|
((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call
|
|
@@ -1800,8 +1807,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
public final BlockPos.MutableBlockPos getRandomBlockPosition(int x, int y, int z, int l, BlockPos.MutableBlockPos out) {
|
|
// Paper end
|
|
- this.randValue = this.randValue * 3 + 1013904223;
|
|
- int i1 = this.randValue >> 2;
|
|
+ int i1 = this.random.nextInt() >> 2; // SparklyPaper - parallel world ticking
|
|
|
|
out.set(x + (i1 & 15), y + (i1 >> 16 & l), z + (i1 >> 8 & 15)); // Paper - change to setValues call
|
|
return out; // Paper
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
|
|
index 644e64850479cea20a98b8a65503ccf3a34fd32a..9013711498a96d8d680e62ee2a3b7ac91c355ab8 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
|
|
@@ -49,7 +49,8 @@ public class DispenserBlock extends BaseEntityBlock {
|
|
object2objectopenhashmap.defaultReturnValue(new DefaultDispenseItemBehavior());
|
|
});
|
|
private static final int TRIGGER_DURATION = 4;
|
|
- public static boolean eventFired = false; // CraftBukkit
|
|
+ // public static boolean eventFired = false; // CraftBukkit // SparklyPaper - parallel world ticking
|
|
+ public static ThreadLocal<Boolean> eventFired = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking
|
|
|
|
@Override
|
|
public MapCodec<? extends DispenserBlock> codec() {
|
|
@@ -105,7 +106,7 @@ public class DispenserBlock extends BaseEntityBlock {
|
|
|
|
if (idispensebehavior != DispenseItemBehavior.NOOP) {
|
|
if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(world, pos, itemstack, i)) return; // Paper - Add BlockPreDispenseEvent
|
|
- DispenserBlock.eventFired = false; // CraftBukkit - reset event status
|
|
+ DispenserBlock.eventFired.set(Boolean.FALSE); // CraftBukkit - reset event status // SparklyPaper - parallel world ticking
|
|
tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack));
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/FungusBlock.java b/src/main/java/net/minecraft/world/level/block/FungusBlock.java
|
|
index 50d7235cf2ef036451db708c45a063d037051ae9..ff9b3d7cbf4ffcc81b4837da0c4eb0d54a52508b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/FungusBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/FungusBlock.java
|
|
@@ -76,9 +76,9 @@ public class FungusBlock extends BushBlock implements BonemealableBlock {
|
|
this.getFeature(world).ifPresent((holder) -> {
|
|
// CraftBukkit start
|
|
if (this == Blocks.WARPED_FUNGUS) {
|
|
- SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS;
|
|
+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.WARPED_FUNGUS); // SparklyPaper - parallel world ticking
|
|
} else if (this == Blocks.CRIMSON_FUNGUS) {
|
|
- SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS;
|
|
+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.CRIMSON_FUNGUS); // SparklyPaper - parallel world ticking
|
|
}
|
|
// CraftBukkit end
|
|
((ConfiguredFeature) holder.value()).place(world, world.getChunkSource().getGenerator(), random, pos);
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/MushroomBlock.java b/src/main/java/net/minecraft/world/level/block/MushroomBlock.java
|
|
index 1f27ae8abd5891a0b8057b454f2210b088b4e95a..7549cdc77c7738d3aba753c51a11e5a6997bd1d8 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/MushroomBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/MushroomBlock.java
|
|
@@ -105,7 +105,7 @@ public class MushroomBlock extends BushBlock implements BonemealableBlock {
|
|
return false;
|
|
} else {
|
|
world.removeBlock(pos, false);
|
|
- SaplingBlock.treeType = (this == Blocks.BROWN_MUSHROOM) ? TreeType.BROWN_MUSHROOM : TreeType.RED_MUSHROOM; // CraftBukkit
|
|
+ SaplingBlock.treeTypeRT.set((this == Blocks.BROWN_MUSHROOM) ? TreeType.BROWN_MUSHROOM : TreeType.RED_MUSHROOM); // CraftBukkit // SparklyPaper - parallel world ticking
|
|
if (((ConfiguredFeature) ((Holder) optional.get()).value()).place(world, world.getChunkSource().getGenerator(), random, pos)) {
|
|
return true;
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
|
|
index b5a71fd4e2f55bf036c2c697da5d50cc90fc657c..16759699834b6318927538e6d46e2f300156fc4f 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
|
|
@@ -261,7 +261,7 @@ public class RedStoneWireBlock extends Block {
|
|
|
|
// Paper start - Optimize redstone (Eigencraft)
|
|
// The bulk of the new functionality is found in RedstoneWireTurbo.java
|
|
- com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo(this);
|
|
+ // com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo(this); // SparklyPaper - parallel world ticking (moved to world)
|
|
|
|
/*
|
|
* Modified version of pre-existing updateSurroundingRedstone, which is called from
|
|
@@ -270,7 +270,7 @@ public class RedStoneWireBlock extends Block {
|
|
*/
|
|
private void updateSurroundingRedstone(Level worldIn, BlockPos pos, BlockState state, BlockPos source) {
|
|
if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
|
|
- turbo.updateSurroundingRedstone(worldIn, pos, state, source);
|
|
+ worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source); // turbo.updateSurroundingRedstone(worldIn, pos, state, source); // SparklyPaper - parallel world ticking
|
|
return;
|
|
}
|
|
updatePowerStrength(worldIn, pos, state);
|
|
@@ -361,7 +361,7 @@ public class RedStoneWireBlock extends Block {
|
|
// [Space Walker] suppress shape updates and emit those manually to
|
|
// bypass the new neighbor update stack.
|
|
if (worldIn.setBlock(pos1, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS))
|
|
- turbo.updateNeighborShapes(worldIn, pos1, state);
|
|
+ worldIn.turbo.updateNeighborShapes(worldIn, pos1, state); // turbo.updateNeighborShapes(worldIn, pos1, state); // SparklyPaper - parallel world ticking
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/SaplingBlock.java b/src/main/java/net/minecraft/world/level/block/SaplingBlock.java
|
|
index 3ff0d08e4964aae82d8e51d3b8bf9aa002096f81..069f669a31b8ac5b90341c7ef066b3a32f75fc3c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/SaplingBlock.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/SaplingBlock.java
|
|
@@ -35,7 +35,7 @@ public class SaplingBlock extends BushBlock implements BonemealableBlock {
|
|
protected static final float AABB_OFFSET = 6.0F;
|
|
protected static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
|
|
protected final TreeGrower treeGrower;
|
|
- public static TreeType treeType; // CraftBukkit
|
|
+ public static final ThreadLocal<TreeType> treeTypeRT = new ThreadLocal<>(); // CraftBukkit // SparklyPaper - parallel world ticking (from Folia)
|
|
|
|
@Override
|
|
public MapCodec<? extends SaplingBlock> codec() {
|
|
@@ -73,8 +73,8 @@ public class SaplingBlock extends BushBlock implements BonemealableBlock {
|
|
this.treeGrower.growTree(world, world.getChunkSource().getGenerator(), pos, state, random);
|
|
world.captureTreeGeneration = false;
|
|
if (world.capturedBlockStates.size() > 0) {
|
|
- TreeType treeType = SaplingBlock.treeType;
|
|
- SaplingBlock.treeType = null;
|
|
+ TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
|
|
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
|
|
Location location = CraftLocation.toBukkit(pos, world.getWorld());
|
|
java.util.List<BlockState> blocks = new java.util.ArrayList<>(world.capturedBlockStates.values());
|
|
world.capturedBlockStates.clear();
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
|
index b8b4d74076fa5ed6eb3b2045384db77e165931b2..331c156c915d47fae67003bd650738ea00492bd3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
|
@@ -78,6 +78,12 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
|
|
return canUnlock(player, lock, containerName, null);
|
|
}
|
|
public static boolean canUnlock(Player player, LockCode lock, Component containerName, @Nullable BlockEntity blockEntity) {
|
|
+ // 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() != serverPlayer.serverLevel()) {
|
|
+ net.minecraft.server.MinecraftServer.LOGGER.warn("Player " + serverPlayer.getScoreboardName() + " (" + serverPlayer.getStringUUID() + ") attempted to open a BlockEntity @ " + blockEntity.getLevel().getWorld().getName() + " " + blockEntity.getBlockPos().getX() + ", " + blockEntity.getBlockPos().getY() + ", " + blockEntity.getBlockPos().getZ() + " while they were in a different world " + serverPlayer.level().getWorld().getName() + " than the block themselves!");
|
|
+ return false;
|
|
+ }
|
|
+ // SparklyPaper end
|
|
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(containerName));
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
|
|
index 83481539e058e5f428d9951e409fed62ef159e5c..c4165376eda073add2a267b737a676123e63a197 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
|
|
+++ b/src/main/java/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 world, BlockPos pos, BlockState state, SculkCatalystBlockEntity blockEntity) {
|
|
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = blockEntity.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.sourceBlockOverrideRT.set(blockEntity.getBlockPos()); // SparklyPaper - parallel world ticking // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
|
|
blockEntity.catalystListener.getSculkSpreader().updateCursors(world, pos, world.getRandom(), true);
|
|
- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverrideRT.set(null); // SparklyPaper - parallel world ticking // CraftBukkit
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/grower/TreeGrower.java b/src/main/java/net/minecraft/world/level/block/grower/TreeGrower.java
|
|
index 8cb822fed0cda4268f288feae1079a3dc4dc103e..1bac955a0b734689cc66b1a61e58fcde4cc3f15b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/grower/TreeGrower.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/grower/TreeGrower.java
|
|
@@ -175,51 +175,53 @@ public final class TreeGrower {
|
|
// CraftBukkit start
|
|
private void setTreeType(Holder<ConfiguredFeature<?, ?>> holder) {
|
|
ResourceKey<ConfiguredFeature<?, ?>> worldgentreeabstract = holder.unwrapKey().get();
|
|
+ TreeType treeType; // SparklyPaper - parallel world ticking
|
|
if (worldgentreeabstract == TreeFeatures.OAK || worldgentreeabstract == TreeFeatures.OAK_BEES_005) {
|
|
- SaplingBlock.treeType = TreeType.TREE;
|
|
+ treeType = TreeType.TREE;
|
|
} else if (worldgentreeabstract == TreeFeatures.HUGE_RED_MUSHROOM) {
|
|
- SaplingBlock.treeType = TreeType.RED_MUSHROOM;
|
|
+ treeType = TreeType.RED_MUSHROOM;
|
|
} else if (worldgentreeabstract == TreeFeatures.HUGE_BROWN_MUSHROOM) {
|
|
- SaplingBlock.treeType = TreeType.BROWN_MUSHROOM;
|
|
+ treeType = TreeType.BROWN_MUSHROOM;
|
|
} else if (worldgentreeabstract == TreeFeatures.JUNGLE_TREE) {
|
|
- SaplingBlock.treeType = TreeType.COCOA_TREE;
|
|
+ treeType = TreeType.COCOA_TREE;
|
|
} else if (worldgentreeabstract == TreeFeatures.JUNGLE_TREE_NO_VINE) {
|
|
- SaplingBlock.treeType = TreeType.SMALL_JUNGLE;
|
|
+ treeType = TreeType.SMALL_JUNGLE;
|
|
} else if (worldgentreeabstract == TreeFeatures.PINE) {
|
|
- SaplingBlock.treeType = TreeType.TALL_REDWOOD;
|
|
+ treeType = TreeType.TALL_REDWOOD;
|
|
} else if (worldgentreeabstract == TreeFeatures.SPRUCE) {
|
|
- SaplingBlock.treeType = TreeType.REDWOOD;
|
|
+ treeType = TreeType.REDWOOD;
|
|
} else if (worldgentreeabstract == TreeFeatures.ACACIA) {
|
|
- SaplingBlock.treeType = TreeType.ACACIA;
|
|
+ treeType = TreeType.ACACIA;
|
|
} else if (worldgentreeabstract == TreeFeatures.BIRCH || worldgentreeabstract == TreeFeatures.BIRCH_BEES_005) {
|
|
- SaplingBlock.treeType = TreeType.BIRCH;
|
|
+ treeType = TreeType.BIRCH;
|
|
} else if (worldgentreeabstract == TreeFeatures.SUPER_BIRCH_BEES_0002) {
|
|
- SaplingBlock.treeType = TreeType.TALL_BIRCH;
|
|
+ treeType = TreeType.TALL_BIRCH;
|
|
} else if (worldgentreeabstract == TreeFeatures.SWAMP_OAK) {
|
|
- SaplingBlock.treeType = TreeType.SWAMP;
|
|
+ treeType = TreeType.SWAMP;
|
|
} else if (worldgentreeabstract == TreeFeatures.FANCY_OAK || worldgentreeabstract == TreeFeatures.FANCY_OAK_BEES_005) {
|
|
- SaplingBlock.treeType = TreeType.BIG_TREE;
|
|
+ treeType = TreeType.BIG_TREE;
|
|
} else if (worldgentreeabstract == TreeFeatures.JUNGLE_BUSH) {
|
|
- SaplingBlock.treeType = TreeType.JUNGLE_BUSH;
|
|
+ treeType = TreeType.JUNGLE_BUSH;
|
|
} else if (worldgentreeabstract == TreeFeatures.DARK_OAK) {
|
|
- SaplingBlock.treeType = TreeType.DARK_OAK;
|
|
+ treeType = TreeType.DARK_OAK;
|
|
} else if (worldgentreeabstract == TreeFeatures.MEGA_SPRUCE) {
|
|
- SaplingBlock.treeType = TreeType.MEGA_REDWOOD;
|
|
+ treeType = TreeType.MEGA_REDWOOD;
|
|
} else if (worldgentreeabstract == TreeFeatures.MEGA_PINE) {
|
|
- SaplingBlock.treeType = TreeType.MEGA_REDWOOD;
|
|
+ treeType = TreeType.MEGA_REDWOOD;
|
|
} else if (worldgentreeabstract == TreeFeatures.MEGA_JUNGLE_TREE) {
|
|
- SaplingBlock.treeType = TreeType.JUNGLE;
|
|
+ treeType = TreeType.JUNGLE;
|
|
} else if (worldgentreeabstract == TreeFeatures.AZALEA_TREE) {
|
|
- SaplingBlock.treeType = TreeType.AZALEA;
|
|
+ treeType = TreeType.AZALEA;
|
|
} else if (worldgentreeabstract == TreeFeatures.MANGROVE) {
|
|
- SaplingBlock.treeType = TreeType.MANGROVE;
|
|
+ treeType = TreeType.MANGROVE;
|
|
} else if (worldgentreeabstract == TreeFeatures.TALL_MANGROVE) {
|
|
- SaplingBlock.treeType = TreeType.TALL_MANGROVE;
|
|
+ treeType = TreeType.TALL_MANGROVE;
|
|
} else if (worldgentreeabstract == TreeFeatures.CHERRY || worldgentreeabstract == TreeFeatures.CHERRY_BEES_005) {
|
|
- SaplingBlock.treeType = TreeType.CHERRY;
|
|
+ treeType = TreeType.CHERRY;
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown tree generator " + worldgentreeabstract);
|
|
}
|
|
+ SaplingBlock.treeTypeRT.set(treeType); // SparklyPaper - parallel world ticking
|
|
}
|
|
// CraftBukkit end
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
index 91b538f047428b8836f7ad3b62a0586fb7beb9ec..9f235ee277bb1b47d55288f0058c0bd94b1afc0c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -419,6 +419,7 @@ public class LevelChunk extends ChunkAccess {
|
|
|
|
@Nullable
|
|
public BlockState setBlockState(BlockPos blockposition, BlockState iblockdata, boolean flag, boolean doPlace) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.level, blockposition, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// CraftBukkit end
|
|
int i = blockposition.getY();
|
|
LevelChunkSection chunksection = this.getSection(this.getSectionIndex(i));
|
|
diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
|
|
index 4cdfc433df67afcd455422e9baf56f167dd712ae..f52b3740bd48f8527a36d48a0454e7d601763985 100644
|
|
--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
|
|
+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
|
|
@@ -9,6 +9,13 @@ import net.minecraft.world.entity.Entity;
|
|
|
|
public class EntityTickList {
|
|
private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking?
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ // Used to track async entity additions/removals/loops
|
|
+ private final net.minecraft.server.level.ServerLevel serverLevel;
|
|
+ public EntityTickList(net.minecraft.server.level.ServerLevel serverLevel) {
|
|
+ this.serverLevel = serverLevel;
|
|
+ }
|
|
+ // SparklyPaper end
|
|
|
|
private void ensureActiveIsNotIterated() {
|
|
// Paper - replace with better logic, do not delay removals
|
|
@@ -16,13 +23,13 @@ public class EntityTickList {
|
|
}
|
|
|
|
public void add(Entity entity) {
|
|
- io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist addition"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.ensureActiveIsNotIterated();
|
|
this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions
|
|
}
|
|
|
|
public void remove(Entity entity) {
|
|
- io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist removal"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.ensureActiveIsNotIterated();
|
|
this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions
|
|
}
|
|
@@ -32,7 +39,7 @@ public class EntityTickList {
|
|
}
|
|
|
|
public void forEach(Consumer<Entity> action) {
|
|
- io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverLevel, "Asynchronous entity ticklist iteration"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
// Paper start - replace with better logic, do not delay removals/additions
|
|
// 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/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index bfb178c69026e9759e9afaebb9da141b62d1f144..295eca9de3e1e5d7eb01f9ff015b525ab728aa31 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -440,7 +440,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
}
|
|
|
|
private boolean unloadChunk0(int x, int z, boolean save) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, x, z, "Cannot unload chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
if (!this.isChunkLoaded(x, z)) {
|
|
return true;
|
|
}
|
|
@@ -455,7 +455,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean regenerateChunk(int x, int z) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("chunk regenerate"); // Spigot
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, x, z, "Cannot regenerate chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
warnUnsafeChunk("regenerating a faraway chunk", x, z); // Paper
|
|
// Paper start - implement regenerateChunk method
|
|
final ServerLevel serverLevel = this.world;
|
|
@@ -518,6 +518,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean refreshChunk(int x, int z) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, x, z, "Cannot refresh chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
|
|
if (playerChunk == null) return false;
|
|
|
|
@@ -553,7 +554,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean loadChunk(int x, int z, boolean generate) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.getHandle(), x, z, "May not sync load chunks asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
|
|
// Paper start - Optimize this method
|
|
ChunkPos chunkPos = new ChunkPos(x, z);
|
|
@@ -813,6 +814,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, loc.getX(), loc.getZ(), "Cannot generate tree asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
this.world.captureTreeGeneration = true;
|
|
this.world.captureBlockStates = true;
|
|
boolean grownTree = this.generateTree(loc, type);
|
|
@@ -923,11 +925,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, x, z, "Cannot create explosion asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
return !this.world.explode(source == null ? null : ((CraftEntity) source).getHandle(), x, y, z, power, setFire, breakBlocks ? net.minecraft.world.level.Level.ExplosionInteraction.MOB : net.minecraft.world.level.Level.ExplosionInteraction.NONE).wasCanceled;
|
|
}
|
|
// Paper start
|
|
@Override
|
|
public boolean createExplosion(Entity source, Location loc, float power, boolean setFire, boolean breakBlocks) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(world, loc.getX(), loc.getZ(), "Cannot create explosion asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
return !world.explode(source != null ? ((org.bukkit.craftbukkit.entity.CraftEntity) source).getHandle() : null, loc.getX(), loc.getY(), loc.getZ(), power, setFire, breakBlocks ? net.minecraft.world.level.Level.ExplosionInteraction.MOB : net.minecraft.world.level.Level.ExplosionInteraction.NONE).wasCanceled;
|
|
}
|
|
// Paper end
|
|
@@ -1004,6 +1008,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, x >> 4, z >> 4, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
warnUnsafeChunk("getting a faraway chunk", x >> 4, z >> 4); // Paper
|
|
// Transient load for this tick
|
|
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
|
|
@@ -1034,6 +1039,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
@Override
|
|
public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> bb) {
|
|
BlockPos pos = new BlockPos(x, 0, z);
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, pos, "Cannot retrieve chunk asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
if (this.world.hasChunkAt(pos)) {
|
|
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos);
|
|
|
|
@@ -2339,6 +2345,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(this.world, position.getX(), position.getZ(), "Cannot send game event asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
getHandle().gameEvent(sourceEntity != null ? ((CraftEntity) sourceEntity).getHandle(): null, net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(gameEvent.getKey())), org.bukkit.craftbukkit.util.CraftVector.toBlockPos(position));
|
|
}
|
|
// Paper end
|
|
@@ -2459,7 +2466,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
// Paper start
|
|
public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
|
|
warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper
|
|
- if (Bukkit.isPrimaryThread()) {
|
|
+ if (io.papermc.paper.util.TickThread.isTickThreadFor(this.getHandle(), x, z)) { // SparklyPaper - parallel world ticking
|
|
net.minecraft.world.level.chunk.LevelChunk immediate = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
|
|
if (immediate != null) {
|
|
return java.util.concurrent.CompletableFuture.completedFuture(new CraftChunk(immediate));
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
|
|
index 4b42ef2a876ea210d948238e63fd7a2b7035bb5b..0631fcf5749bfd7ca4ba8bad8a959fb024a1d017 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
|
|
@@ -75,6 +75,11 @@ public class CraftBlock implements Block {
|
|
}
|
|
|
|
public net.minecraft.world.level.block.state.BlockState getNMS() {
|
|
+ // Folia start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // Folia end - parallel world ticking
|
|
return this.world.getBlockState(this.position);
|
|
}
|
|
|
|
@@ -151,6 +156,11 @@ public class CraftBlock implements Block {
|
|
}
|
|
|
|
private void setData(final byte data, int flag) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flag);
|
|
}
|
|
|
|
@@ -192,6 +202,12 @@ public class CraftBlock implements Block {
|
|
}
|
|
|
|
public static boolean setTypeAndData(LevelAccessor world, BlockPos position, net.minecraft.world.level.block.state.BlockState old, net.minecraft.world.level.block.state.BlockState blockData, boolean applyPhysics) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
+
|
|
// SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup
|
|
if (old.hasBlockEntity() && blockData.getBlock() != old.getBlock()) { // SPIGOT-3725 remove old tile entity if block changes
|
|
// SPIGOT-4612: faster - just clear tile
|
|
@@ -337,18 +353,33 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public Biome getBiome() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ());
|
|
}
|
|
|
|
// Paper start
|
|
@Override
|
|
public Biome getComputedBiome() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ());
|
|
}
|
|
// Paper end
|
|
|
|
@Override
|
|
public void setBiome(Biome bio) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
|
|
}
|
|
|
|
@@ -396,6 +427,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean isBlockFaceIndirectlyPowered(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
int power = this.world.getMinecraftWorld().getSignal(this.position, CraftBlock.blockFaceToNotch(face));
|
|
|
|
Block relative = this.getRelative(face);
|
|
@@ -408,6 +444,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public int getBlockPower(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
int power = 0;
|
|
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
|
|
int x = this.getX();
|
|
@@ -478,6 +519,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean breakNaturally() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
return this.breakNaturally(null);
|
|
}
|
|
|
|
@@ -537,6 +583,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean applyBoneMeal(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
Direction direction = CraftBlock.blockFaceToNotch(face);
|
|
BlockFertilizeEvent event = null;
|
|
ServerLevel world = this.getCraftWorld().getHandle();
|
|
@@ -548,8 +599,8 @@ public class CraftBlock implements Block {
|
|
world.captureTreeGeneration = false;
|
|
|
|
if (world.capturedBlockStates.size() > 0) {
|
|
- TreeType treeType = SaplingBlock.treeType;
|
|
- SaplingBlock.treeType = null;
|
|
+ TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking
|
|
+ SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking
|
|
List<BlockState> blocks = new ArrayList<>(world.capturedBlockStates.values());
|
|
world.capturedBlockStates.clear();
|
|
StructureGrowEvent structureEvent = null;
|
|
@@ -638,6 +689,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
Preconditions.checkArgument(start != null, "Location start cannot be null");
|
|
Preconditions.checkArgument(this.getWorld().equals(start.getWorld()), "Location start cannot be a different world");
|
|
start.checkFinite();
|
|
@@ -679,6 +735,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean canPlace(BlockData data) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
Preconditions.checkArgument(data != null, "BlockData cannot be null");
|
|
net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState();
|
|
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
|
|
@@ -713,6 +774,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
net.minecraft.world.level.block.state.BlockState blockData = this.getNMS();
|
|
net.minecraft.server.level.ServerLevel level = this.world.getMinecraftWorld();
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
|
index 7cd0fae59c497861063827eda4243cc6c11e7cff..8d2f84442b8872224a0b3701dbd9876f54890efb 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
|
@@ -18,7 +18,7 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
|
|
private final T tileEntity;
|
|
private final T snapshot;
|
|
public boolean snapshotDisabled; // Paper
|
|
- public static boolean DISABLE_SNAPSHOT = false; // Paper
|
|
+ public static ThreadLocal<Boolean> DISABLE_SNAPSHOT = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking
|
|
|
|
public CraftBlockEntityState(World world, T tileEntity) {
|
|
super(world, tileEntity.getBlockPos(), tileEntity.getBlockState());
|
|
@@ -27,8 +27,8 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
|
|
|
|
try { // Paper - Show blockstate location if we failed to read it
|
|
// Paper start
|
|
- this.snapshotDisabled = DISABLE_SNAPSHOT;
|
|
- if (DISABLE_SNAPSHOT) {
|
|
+ this.snapshotDisabled = DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking
|
|
+ if (DISABLE_SNAPSHOT.get()) { // SparklyPaper - parallel world ticking
|
|
this.snapshot = this.tileEntity;
|
|
} else {
|
|
this.snapshot = this.createSnapshot(tileEntity);
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
|
|
index d11d854ae90b99aa2ce227994d07fe1fe9b4a60a..3040588155878dcbeb4fb5d71cf99b83f88e0a82 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
|
|
@@ -210,6 +210,12 @@ public class CraftBlockState implements BlockState {
|
|
LevelAccessor access = this.getWorldHandle();
|
|
CraftBlock block = this.getBlock();
|
|
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (access instanceof net.minecraft.server.level.ServerLevel serverWorld) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
+
|
|
if (block.getType() != this.getType()) {
|
|
if (!force) {
|
|
return false;
|
|
@@ -350,6 +356,7 @@ public class CraftBlockState implements BlockState {
|
|
|
|
@Override
|
|
public java.util.Collection<org.bukkit.inventory.ItemStack> getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) {
|
|
+ io.papermc.paper.util.TickThread.ensureTickThread(world.getHandle(), position, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking
|
|
net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item);
|
|
|
|
// Modelled off EntityHuman#hasBlock
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
|
index 8afc396c162d928902a9d9beb9f039b06630f755..4cde4be86dc03ebbfb5c12a910acbf9978fd42d4 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
|
@@ -242,8 +242,8 @@ public final class CraftBlockStates {
|
|
net.minecraft.world.level.block.state.BlockState blockData = craftBlock.getNMS();
|
|
BlockEntity tileEntity = craftBlock.getHandle().getBlockEntity(blockPosition);
|
|
// Paper start - block state snapshots
|
|
- boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
|
|
- CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
|
|
+ boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking
|
|
+ CraftBlockEntityState.DISABLE_SNAPSHOT.set(!useSnapshot); // SparklyPaper - parallel world ticking
|
|
try {
|
|
// Paper end
|
|
CraftBlockState blockState = CraftBlockStates.getBlockState(world, blockPosition, blockData, tileEntity);
|
|
@@ -251,7 +251,7 @@ public final class CraftBlockStates {
|
|
return blockState;
|
|
// Paper start
|
|
} finally {
|
|
- CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
|
|
+ CraftBlockEntityState.DISABLE_SNAPSHOT.set(prev); // SparklyPaper - parallel world ticking
|
|
}
|
|
// Paper end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
index cb3e9672f375a1a660757a05362729ddb5ca7504..6a6ae69a5b33a16612ee7062cd6c774ebb3f6e41 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -942,7 +942,7 @@ public class CraftEventFactory {
|
|
return CraftEventFactory.handleBlockSpreadEvent(world, source, target, block, 2);
|
|
}
|
|
|
|
- public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
|
|
+ public static final ThreadLocal<BlockPos> sourceBlockOverrideRT = new ThreadLocal<>(); // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep. // SparklyPaper - parallel world ticking (this is from Folia, fixes concurrency bugs with sculk catalysts)
|
|
|
|
public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState block, int flag) {
|
|
// Suppress during worldgen
|
|
@@ -954,7 +954,7 @@ public class CraftEventFactory {
|
|
CraftBlockState state = CraftBlockStates.getBlockState(world, target, flag);
|
|
state.setData(block);
|
|
|
|
- BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), state);
|
|
+ BlockSpreadEvent event = new BlockSpreadEvent(state.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverrideRT.get() != null ? CraftEventFactory.sourceBlockOverrideRT.get() : source), state); // SparklyPaper - parallel world ticking
|
|
Bukkit.getPluginManager().callEvent(event);
|
|
|
|
if (!event.isCancelled()) {
|
|
@@ -2140,7 +2140,7 @@ public class CraftEventFactory {
|
|
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemStack.copyWithCount(1));
|
|
|
|
org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), CraftVector.toBukkit(to));
|
|
- if (!net.minecraft.world.level.block.DispenserBlock.eventFired) {
|
|
+ if (!net.minecraft.world.level.block.DispenserBlock.eventFired.get()) { // SparklyPaper - parallel world ticking
|
|
if (!event.callEvent()) {
|
|
return itemStack;
|
|
}
|
|
diff --git a/src/main/kotlin/net/sparklypower/sparklypaper/ServerLevelTickExecutorThreadFactory.kt b/src/main/kotlin/net/sparklypower/sparklypaper/ServerLevelTickExecutorThreadFactory.kt
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3d536f724ffdae462e3af39e85e4e39190696c37
|
|
--- /dev/null
|
|
+++ b/src/main/kotlin/net/sparklypower/sparklypaper/ServerLevelTickExecutorThreadFactory.kt
|
|
@@ -0,0 +1,21 @@
|
|
+package net.sparklypower.sparklypaper
|
|
+
|
|
+import io.papermc.paper.util.TickThread
|
|
+import java.util.concurrent.ThreadFactory
|
|
+import java.util.concurrent.atomic.AtomicInteger
|
|
+
|
|
+class ServerLevelTickExecutorThreadFactory(private val worldName: String) : ThreadFactory {
|
|
+ override fun newThread(p0: Runnable): Thread {
|
|
+ val tickThread = TickThread.ServerLevelTickThread(p0, "serverlevel-tick-worker [$worldName]")
|
|
+
|
|
+ if (tickThread.isDaemon) {
|
|
+ tickThread.isDaemon = false
|
|
+ }
|
|
+
|
|
+ if (tickThread.priority != 5) {
|
|
+ tickThread.priority = 5
|
|
+ }
|
|
+
|
|
+ return tickThread
|
|
+ }
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/src/main/kotlin/net/sparklypower/sparklypaper/configs/SparklyPaperConfigUtils.kt b/src/main/kotlin/net/sparklypower/sparklypaper/configs/SparklyPaperConfigUtils.kt
|
|
index 8b78f0e8b1de1a6a2506e686be9d71ced72352dd..ce608ca9640cdea0fe690ef61021355822284cf6 100644
|
|
--- a/src/main/kotlin/net/sparklypower/sparklypaper/configs/SparklyPaperConfigUtils.kt
|
|
+++ b/src/main/kotlin/net/sparklypower/sparklypaper/configs/SparklyPaperConfigUtils.kt
|
|
@@ -17,6 +17,7 @@ object SparklyPaperConfigUtils {
|
|
)
|
|
)
|
|
lateinit var config: SparklyPaperConfig
|
|
+ val logContainerCreationStacktraces = java.lang.Boolean.getBoolean("sparklypaper.logContainerCreationStacktraces")
|
|
|
|
fun init(configFile: File) {
|
|
// Write default config if the file doesn't exist
|