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] Parallel world ticking 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..c153e79ebe1f2338f0d0ca6b45b392794ecd2fd7 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.DivineConfig.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.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/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java index a9ec83b5bcb329bf3d2f3fb0e502685a37f9dcbc..adf526bcb5b4df3b798a8b80ad11b7b2c30775d7 100644 --- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java @@ -28,6 +28,7 @@ import java.util.logging.Level; class PaperEventManager { private final Server server; + private final org.purpurmc.purpur.util.MinecraftInternalPlugin minecraftInternalPlugin = new org.purpurmc.purpur.util.MinecraftInternalPlugin(); // DivineMC - Parallel world ticking public PaperEventManager(Server server) { this.server = server; @@ -40,6 +41,12 @@ class PaperEventManager { if (listeners.length == 0) return; // DivineMC end - Skip event if no listeners if (event.isAsynchronous() && this.server.isPrimaryThread()) { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking && org.bxteam.divinemc.DivineConfig.pwtCompatabilityMode) { + org.bukkit.Bukkit.getAsyncScheduler().runNow(minecraftInternalPlugin, task -> event.callEvent()); + return; + } + // DivineMC end - Parallel world ticking throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { // DivineMC start - Multithreaded Tracker diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 0ea7df63498492e320f98ddec5bdf33b203cc770..9418a1771287142950d31c3bf31c85c8e7848dc4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -447,7 +447,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.DivineConfig.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); } @@ -473,6 +479,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public boolean refreshChunk(int x, int z) { + if (org.bxteam.divinemc.DivineConfig.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; @@ -523,7 +530,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.DivineConfig.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 @@ -751,6 +764,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) { + if (org.bxteam.divinemc.DivineConfig.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); @@ -866,6 +880,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { } public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source, Consumer configurator) { // Paper end - expand explosion API + if (org.bxteam.divinemc.DivineConfig.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 @@ -957,6 +972,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) { + if (org.bxteam.divinemc.DivineConfig.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); @@ -987,6 +1003,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public void setBiome(int x, int y, int z, Holder bb) { BlockPos pos = new BlockPos(x, 0, z); + if (org.bxteam.divinemc.DivineConfig.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); @@ -2295,6 +2312,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) { + if (org.bxteam.divinemc.DivineConfig.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 diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java index a4d5c65edc1db59f3486ce5d3757cc306211a54b..427719a50edc89f4bb7214577279f7464764bf9e 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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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.DivineConfig.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); } @@ -542,6 +587,11 @@ public class CraftBlock implements Block { @Override public boolean applyBoneMeal(BlockFace face) { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.DivineConfig.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(); @@ -553,8 +603,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 states = new ArrayList<>(world.capturedBlockStates.values()); world.capturedBlockStates.clear(); StructureGrowEvent structureEvent = null; @@ -644,6 +696,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.DivineConfig.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(); @@ -685,6 +742,11 @@ public class CraftBlock implements Block { @Override public boolean canPlace(BlockData data) { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.DivineConfig.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(); @@ -724,6 +786,11 @@ public class CraftBlock implements Block { @Override public void tick() { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.DivineConfig.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 3422970353dcd886934b9ee906467769d39abbde..cb60c1b09f06872c07d05b04b7bacc5921fea78d 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -26,6 +26,25 @@ public abstract class CraftBlockEntityState 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 DISABLE_SNAPSHOT_TL = ThreadLocal.withInitial(() -> Boolean.FALSE); + + public static boolean getDisableSnapshotTL() { + if (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking && DISABLE_SNAPSHOT_TL.get()) return true; + + synchronized (CraftBlockEntityState.class) { + return DISABLE_SNAPSHOT; + } + } + + public static void setDisableSnapshotTL(boolean value) { + if (org.bxteam.divinemc.DivineConfig.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()); @@ -34,8 +53,10 @@ public abstract class CraftBlockEntityState 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..bf70de08a9f5d49905b253bf549f6aeb8e2d3250 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.DivineConfig.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 getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) { + if (org.bxteam.divinemc.DivineConfig.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 2338e7c115037430cefae26a571ded71f77983c4..6d5af7f72158e9e4abbfb6ae650833a66ea7a934 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java @@ -195,14 +195,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/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java index e4e2e42d0ca25df7fe9f2dd4275610e45fcb2c84..8d0816dcc4159b62e5bbb03e3897b255feb961e6 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java @@ -19,11 +19,28 @@ class CraftAsyncTask extends CraftTask { @Override public boolean isSync() { - return false; + return org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking && org.bxteam.divinemc.DivineConfig.pwtCompatabilityMode; // DivineMC - Parallel world ticking } @Override public void run() { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking && org.bxteam.divinemc.DivineConfig.pwtCompatabilityMode) { + try { + super.run(); + } catch (final Throwable t) { + this.getOwner().getLogger().log( + Level.WARNING, + String.format( + "Plugin %s generated an exception while executing task %s (sync mode)", + this.getOwner().getDescription().getFullName(), + this.getTaskId()), + t); + } + return; + } + // DivineMC end - Parallel world ticking + final Thread thread = Thread.currentThread(); // Paper start - name threads according to running plugin final String nameBefore = thread.getName();