9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00
Files
DivineMC/divinemc-server/paper-patches/features/0016-SparklyPaper-Parallel-world-ticking.patch
NONPLAYT 1d79525ed6 Updated Upstream (Purpur)
Upstream has released updates that appear to apply and compile correctly

Purpur Changes:
PurpurMC/Purpur@fc89fde5 Updated Upstream (Paper)
2025-11-03 01:57:37 +03:00

625 lines
36 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
Date: Wed, 29 Jan 2025 01:41:25 +0300
Subject: [PATCH] SparklyPaper: Parallel world ticking
Original project: https://github.com/SparklyPower/SparklyPaper
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
index 69cdd304d255d52c9b7dc9b6a33ffdb630b79abe..73475f21a65395ab3f888d04d4860acde8aeb09c 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
@@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
+ public static final boolean HARD_THROW = !org.bxteam.divinemc.config.DivineConfig.AsyncCategory.disableHardThrow; // DivineMC - Parallel world ticking
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
@@ -26,14 +27,14 @@ public class TickThread extends Thread {
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
- throw new IllegalStateException(reason);
+ if (HARD_THROW) throw new IllegalStateException(reason); // DivineMC - Parallel world ticking
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + " - " + getTickThreadInformation(world.getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -42,7 +43,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius + " - " + getTickThreadInformation(world.getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -60,7 +61,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ) + " - " + getTickThreadInformation(world.getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -69,7 +70,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
+ reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity) + " - " + getTickThreadInformation(entity.level().getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -78,7 +79,7 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb + " - " + getTickThreadInformation(world.getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -87,12 +88,67 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
final String ex = "Thread failed main thread check: " +
- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ) + " - " + getTickThreadInformation(world.getServer()); // DivineMC - Parallel world ticking
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
+ // DivineMC start - Parallel world ticking
+ public static void ensureTickThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ if (!isTickThreadFor(world)) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable());
+ if (HARD_THROW) throw new IllegalStateException(reason);
+ }
+ }
+
+ public static void ensureOnlyTickThread(final String reason) {
+ boolean isTickThread = isTickThread();
+ boolean isServerLevelTickThread = isServerLevelTickThread();
+ if (!isTickThread || isServerLevelTickThread) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread ONLY tick thread check: " + reason, new Throwable());
+ if (HARD_THROW) throw new IllegalStateException(reason);
+ }
+ }
+
+ public static void ensureTickThreadOrAsyncThread(final net.minecraft.server.level.ServerLevel world, final String reason) {
+ boolean isValidTickThread = isTickThreadFor(world);
+ boolean isAsyncThread = !isTickThread();
+ boolean isValid = isAsyncThread || isValidTickThread;
+ if (!isValid) {
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread or async thread check: " + reason + " @ world " + world.getWorld().getName() + " - " + getTickThreadInformation(world.getServer()), new Throwable());
+ if (HARD_THROW)
+ throw new IllegalStateException(reason);
+ }
+ }
+
+ public static String getTickThreadInformation(net.minecraft.server.MinecraftServer minecraftServer) {
+ StringBuilder sb = new StringBuilder();
+ Thread currentThread = Thread.currentThread();
+ sb.append("Is tick thread? ");
+ sb.append(currentThread instanceof TickThread);
+ sb.append("; Is server level tick thread? ");
+ sb.append(currentThread instanceof ServerLevelTickThread);
+ if (currentThread instanceof ServerLevelTickThread serverLevelTickThread) {
+ sb.append("; Currently ticking level: ");
+ if (serverLevelTickThread.currentlyTickingServerLevel != null) {
+ sb.append(serverLevelTickThread.currentlyTickingServerLevel.getWorld().getName());
+ } else {
+ sb.append("null");
+ }
+ }
+ sb.append("; Is iterating over levels? ");
+ sb.append(minecraftServer.isIteratingOverLevels);
+ sb.append("; Are we going to hard throw? ");
+ sb.append(HARD_THROW);
+ return sb.toString();
+ }
+
+ public static boolean isServerLevelTickThread() {
+ return Thread.currentThread() instanceof ServerLevelTickThread;
+ }
+ // DivineMC end - Parallel world ticking
+
public final int id; /* We don't override getId as the spec requires that it be unique (with respect to all other threads) */
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
@@ -126,47 +182,71 @@ public class TickThread extends Thread {
return false;
}
+ // DivineMC start - Parallel world ticking
public static boolean isTickThreadFor(final Level world, final BlockPos pos) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final Vec3 pos) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final AABB aabb) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final double blockX, final double blockZ) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final Vec3 position, final Vec3 deltaMovement, final int buffer) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final int fromChunkX, final int fromChunkZ, final int toChunkX, final int toChunkZ) {
- return isTickThread();
+ return isTickThreadFor(world);
}
public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ, final int radius) {
- return isTickThread();
+ return isTickThreadFor(world);
+ }
+
+ public static boolean isTickThreadFor(final Level world) {
+ if (Thread.currentThread() instanceof ServerLevelTickThread serverLevelTickThread) {
+ return serverLevelTickThread.currentlyTickingServerLevel == world;
+ } else return isTickThread();
}
public static boolean isTickThreadFor(final Entity entity) {
- return isTickThread();
+ if (entity == null) {
+ return true;
+ }
+
+ return isTickThreadFor(entity.level());
+ }
+
+ public static class ServerLevelTickThread extends TickThread {
+ public ServerLevelTickThread(String name) {
+ super(name);
+ }
+
+ public ServerLevelTickThread(Runnable run, String name) {
+ super(run, name);
+ }
+
+ public net.minecraft.server.level.ServerLevel currentlyTickingServerLevel;
}
+ // DivineMC end - Parallel world ticking
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index b538a051de94c421fc4abeb1d3538f381081fd81..2565340258001c086aa23b018a4d7b3e4c89457b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -3066,4 +3066,11 @@ public final class CraftServer implements Server {
return getServer().lagging;
}
// Purpur end - Lagging threshold
+
+ // DivineMC start - Parallel world ticking
+ @Override
+ public boolean isParallelWorldTickingEnabled() {
+ return org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking;
+ }
+ // DivineMC end - Parallel world ticking
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index b65a086a63a9fe1ea1810d3e4c614a6650cad50d..2136372fd19aa89476b9b54b07765c5a0e83596c 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -463,7 +463,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean unloadChunkRequest(int x, int z) {
- org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot unload chunk asynchronously");
+ } else {
+ org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
+ }
+ // DivineMC end - Parallel world ticking
if (this.isChunkLoaded(x, z)) {
this.world.getChunkSource().removeTicketWithRadius(TicketType.PLUGIN, new ChunkPos(x, z), 1);
}
@@ -489,6 +495,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean refreshChunk(int x, int z) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot refresh chunk asynchronously"); // DivineMC - Parallel world ticking
ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
if (playerChunk == null) return false;
@@ -539,7 +546,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
- org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.getHandle(), x, z, "May not sync load chunks asynchronously");
+ } else {
+ org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
+ }
+ // DivineMC end - Parallel world ticking
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
@@ -752,6 +765,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, loc.getX(), loc.getZ(), "Cannot generate tree asynchronously"); // DivineMC - Parallel world ticking
this.world.captureTreeGeneration = true;
this.world.captureBlockStates = true;
boolean grownTree = this.generateTree(loc, type);
@@ -853,6 +867,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
private boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer<net.minecraft.world.level.ServerExplosion> configurator) {
// Paper end - expand explosion API
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot create explosion asynchronously"); // DivineMC - Parallel world ticking
net.minecraft.world.level.Level.ExplosionInteraction explosionType;
if (!breakBlocks) {
explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks
@@ -909,6 +924,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x >> 4, z >> 4, "Cannot retrieve chunk asynchronously"); // DivineMC - Parallel world ticking
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);
@@ -924,6 +940,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);
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, pos, "Cannot retrieve chunk asynchronously"); // DivineMC - Parallel world ticking
if (this.world.hasChunkAt(pos)) {
net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos);
@@ -1903,6 +1920,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, position.getX(), position.getZ(), "Cannot send game event asynchronously"); // DivineMC - Parallel world ticking
getHandle().gameEvent(sourceEntity != null ? ((CraftEntity) sourceEntity).getHandle(): null, net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(gameEvent.getKey())).orElseThrow(), org.bukkit.craftbukkit.util.CraftVector.toBlockPos(position));
}
// Paper end
@@ -2113,4 +2131,15 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return POINTERS_SUPPLIER.view(this);
}
// Paper end
+
+ // DivineMC start - Parallel world ticking
+ @Override
+ public long @NotNull [] getTickTimes() {
+ return this.world.tickTimes5s.getTimes();
+ }
+ @Override
+ public double getAverageTickTime() {
+ return this.world.tickTimes5s.getAverage();
+ }
+ // DivineMC end - Parallel world ticking
}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 52b3362259ed1ba2ce5379230b2c3b06b0ff6249..1d8c9f94198b558a6b4cdc5d4981b6b4e946903b 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() {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
return this.world.getBlockState(this.position);
}
@@ -155,6 +160,11 @@ public class CraftBlock implements Block {
}
private void setData(final byte data, int flags) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flags);
}
@@ -196,6 +206,11 @@ public class CraftBlock implements Block {
}
public static boolean setBlockState(LevelAccessor world, BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, boolean applyPhysics) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, pos, "Cannot modify world asynchronously");
+ }
+ // DivineMC 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 block entity cleanup
if (oldState.hasBlockEntity() && newState.getBlock() != oldState.getBlock()) { // SPIGOT-3725 remove old block entity if block changes
// SPIGOT-4612: faster - just clear tile
@@ -344,18 +359,33 @@ public class CraftBlock implements Block {
@Override
public Biome getBiome() {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ());
}
// Paper start
@Override
public Biome getComputedBiome() {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ());
}
// Paper end
@Override
public void setBiome(Biome bio) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
}
@@ -403,6 +433,11 @@ public class CraftBlock implements Block {
@Override
public boolean isBlockFaceIndirectlyPowered(BlockFace face) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
int power = this.world.getMinecraftWorld().getSignal(this.position, CraftBlock.blockFaceToNotch(face));
Block relative = this.getRelative(face);
@@ -415,6 +450,11 @@ public class CraftBlock implements Block {
@Override
public int getBlockPower(BlockFace face) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
int power = 0;
net.minecraft.world.level.Level world = this.world.getMinecraftWorld();
int x = this.getX();
@@ -483,6 +523,11 @@ public class CraftBlock implements Block {
@Override
public boolean breakNaturally() {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
return this.breakNaturally(null);
}
@@ -548,6 +593,11 @@ public class CraftBlock implements Block {
@Override
public boolean applyBoneMeal(BlockFace face) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
Direction direction = CraftBlock.blockFaceToNotch(face);
BlockFertilizeEvent event = null;
ServerLevel world = this.getCraftWorld().getHandle();
@@ -559,8 +609,10 @@ public class CraftBlock implements Block {
world.captureTreeGeneration = false;
if (!world.capturedBlockStates.isEmpty()) {
- TreeType treeType = SaplingBlock.treeType;
- SaplingBlock.treeType = null;
+ // DivineMC start - Parallel world ticking
+ TreeType treeType = SaplingBlock.getTreeTypeTL();
+ SaplingBlock.setTreeTypeTL(null);
+ // DivineMC end - Parallel world ticking
List<BlockState> states = new ArrayList<>(world.capturedBlockStates.values());
world.capturedBlockStates.clear();
StructureGrowEvent structureEvent = null;
@@ -650,6 +702,11 @@ public class CraftBlock implements Block {
@Override
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC 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();
@@ -691,6 +748,11 @@ public class CraftBlock implements Block {
@Override
public boolean canPlace(BlockData data) {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
+ }
+ // DivineMC 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();
@@ -730,6 +792,11 @@ public class CraftBlock implements Block {
@Override
public void tick() {
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && world instanceof ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
final ServerLevel level = this.world.getMinecraftWorld();
this.getNMS().tick(level, this.position, level.random);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index 5d4faad9df4824cfd61abfd4df011c006f114424..361ddcfaaa47f27135fd4629446b6560b60badeb 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -33,6 +33,25 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
private final T snapshot;
public boolean snapshotDisabled; // Paper
public static boolean DISABLE_SNAPSHOT = false; // Paper
+ // DivineMC start - Parallel world ticking
+ public static ThreadLocal<Boolean> DISABLE_SNAPSHOT_TL = ThreadLocal.withInitial(() -> Boolean.FALSE);
+
+ public static boolean getDisableSnapshotTL() {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && DISABLE_SNAPSHOT_TL.get()) return true;
+
+ synchronized (CraftBlockEntityState.class) {
+ return DISABLE_SNAPSHOT;
+ }
+ }
+
+ public static void setDisableSnapshotTL(boolean value) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) DISABLE_SNAPSHOT_TL.set(value);
+
+ synchronized (CraftBlockEntityState.class) {
+ DISABLE_SNAPSHOT = value;
+ }
+ }
+ // DivineMC end - Parallel world ticking
public CraftBlockEntityState(World world, T blockEntity) {
super(world, blockEntity.getBlockPos(), blockEntity.getBlockState());
@@ -41,8 +60,10 @@ 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) {
+ // DivineMC start - Parallel world ticking
+ this.snapshotDisabled = getDisableSnapshotTL();
+ if (snapshotDisabled) {
+ // DivineMC end - Parallel world ticking
this.snapshot = this.blockEntity;
} else {
this.snapshot = this.createSnapshot(blockEntity);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
index 196835bdf95ba0e149b2977e9ef41698971f501f..b35dbe2b6e75ec89483aef093474c6757983a73f 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
@@ -218,6 +218,12 @@ public class CraftBlockState implements BlockState {
LevelAccessor access = this.getWorldHandle();
CraftBlock block = this.getBlock();
+ // DivineMC start - Parallel world ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && access instanceof net.minecraft.server.level.ServerLevel serverWorld) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
+ }
+ // DivineMC end - Parallel world ticking
+
if (block.getType() != this.getType()) {
if (!force) {
return false;
@@ -365,6 +371,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) {
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(world.getHandle(), position, "Cannot modify world asynchronously"); // DivineMC - Parallel world ticking
this.requirePlaced();
net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
index 03418a9d7cca79a6fa682b64f9d901a4af3e1d58..f0af6319089b477c5d63aec521aee4cc69b52f23 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
@@ -196,14 +196,16 @@ public final class CraftBlockStates {
BlockPos pos = craftBlock.getPosition();
net.minecraft.world.level.block.state.BlockState state = craftBlock.getNMS();
BlockEntity blockEntity = craftBlock.getHandle().getBlockEntity(pos);
- boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT;
- CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot;
+ // DivineMC start - Parallel world ticking
+ boolean prev = CraftBlockEntityState.getDisableSnapshotTL();
+ CraftBlockEntityState.setDisableSnapshotTL(!useSnapshot);
+ // DivineMC end - Parallel world ticking
try {
CraftBlockState blockState = CraftBlockStates.getBlockState(world, pos, state, blockEntity);
blockState.setWorldHandle(craftBlock.getHandle()); // Inject the block's generator access
return blockState;
} finally {
- CraftBlockEntityState.DISABLE_SNAPSHOT = prev;
+ CraftBlockEntityState.setDisableSnapshotTL(prev); // DivineMC - Parallel world ticking
}
}
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 0e5b10153821fda6056791e1c216d05a9ac8e5bc..89e12d30d0815d93842b59642178b4558ee9aae3 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -112,6 +112,18 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.threadId(), Integer.MAX_VALUE), logger);
logger.log(Level.SEVERE, "------------------------------");
+ // DivineMC start - Dump Parallel World Ticking thread
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
+ logger.log(Level.SEVERE, "Parallel world ticking thread dump");
+ for (Thread thread : org.apache.commons.lang3.ThreadUtils.getAllThreads()) {
+ if (thread instanceof ServerLevelTickThread tickThread) {
+ WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(tickThread.threadId(), Integer.MAX_VALUE), logger);
+ }
+ }
+ logger.log(Level.SEVERE, "------------------------------");
+ }
+ // DivineMC end - Dump Parallel World Ticking thread
+
// Paper start - Only print full dump on long timeouts
if (isLongTimeout) {
logger.log(Level.SEVERE, "Entire Thread Dump:");