mirror of
https://github.com/SparklyPower/SparklyPaper.git
synced 2025-12-19 15:09:27 +00:00
628 lines
36 KiB
Diff
628 lines
36 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/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
|
|
index 157e5edb507d6d2a922833c70a1c27abc93c9c34..f47b49b02f664725d310887e65477deb07414bde 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 = !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
|
|
|
|
/**
|
|
* @deprecated
|
|
@@ -22,14 +23,15 @@ public class TickThread extends Thread {
|
|
public static void ensureTickThread(final String reason) {
|
|
if (!isTickThread()) {
|
|
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
|
- throw new IllegalStateException(reason);
|
|
+ if (HARD_THROW)
|
|
+ throw new IllegalStateException(reason);
|
|
}
|
|
}
|
|
|
|
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
|
|
if (!isTickThreadFor(world, pos)) {
|
|
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
|
|
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + " - " + getTickThreadInformation(world.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
@@ -38,7 +40,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 " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
|
|
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius + " - " + getTickThreadInformation(world.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
@@ -56,7 +58,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 " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
|
|
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ) + " - " + getTickThreadInformation(world.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
@@ -65,7 +67,7 @@ public class TickThread extends Thread {
|
|
public static void ensureTickThread(final Entity entity, final String reason) {
|
|
if (!isTickThreadFor(entity)) {
|
|
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", entity=" + entity;
|
|
+ reason + ", entity=" + entity + " - " + getTickThreadInformation(entity.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
@@ -74,7 +76,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 " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
|
|
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb + " - " + getTickThreadInformation(world.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
@@ -83,12 +85,74 @@ 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 " + Thread.currentThread().getName() + " failed main thread check: " +
|
|
- reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
|
|
+ reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ) + " - " + getTickThreadInformation(world.getServer());
|
|
LOGGER.error(ex, new Throwable());
|
|
throw new IllegalStateException(ex);
|
|
}
|
|
}
|
|
|
|
+ // 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 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);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // 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) {
|
|
+ LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread ONLY tick thread check: " + reason, 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 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;
|
|
+ }
|
|
+
|
|
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();
|
|
@@ -123,7 +187,11 @@ public class TickThread extends Thread {
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final Level 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 Level world, final BlockPos pos, final int blockRadius) {
|
|
@@ -131,38 +199,103 @@ public class TickThread extends Thread {
|
|
}
|
|
|
|
public static boolean isTickThreadFor(final Level 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 Level 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 Level 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 Level 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 Level 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 Level 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 Level 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 Level 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 Level 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 net.minecraft.server.level.ServerLevel currentlyTickingServerLevel;
|
|
}
|
|
+ // SparklyPaper end
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index ba32db69c95b325a781cb2dff4200e4464a11baf..219a87300838cb4d078553dda666b3207f8191b8 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -457,7 +457,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
}
|
|
|
|
private boolean unloadChunk0(int x, int z, boolean save) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
|
|
+ ca.spottedleaf.moonrise.common.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;
|
|
}
|
|
@@ -480,6 +480,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
if (!unloadChunk0(x, z, false)) {
|
|
return false;
|
|
}
|
|
+ ca.spottedleaf.moonrise.common.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
|
|
|
|
final long chunkKey = ChunkCoordIntPair.pair(x, z);
|
|
@@ -501,6 +502,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean refreshChunk(int x, int z) {
|
|
+ ca.spottedleaf.moonrise.common.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;
|
|
|
|
@@ -551,7 +553,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
|
|
+ ca.spottedleaf.moonrise.common.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
|
|
ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
|
|
|
|
@@ -779,6 +781,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) {
|
|
+ ca.spottedleaf.moonrise.common.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);
|
|
@@ -894,6 +897,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<net.minecraft.world.level.ServerExplosion> configurator) {
|
|
// Paper end - expand explosion API
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, x, z, "Cannot create explosion asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs)
|
|
net.minecraft.world.level.Level.ExplosionInteraction explosionType;
|
|
if (!breakBlocks) {
|
|
explosionType = net.minecraft.world.level.Level.ExplosionInteraction.NONE; // Don't break blocks
|
|
@@ -985,6 +989,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
|
|
+ ca.spottedleaf.moonrise.common.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);
|
|
@@ -1015,6 +1020,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);
|
|
+ ca.spottedleaf.moonrise.common.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);
|
|
|
|
@@ -2292,6 +2298,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
|
|
|
@Override
|
|
public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) {
|
|
+ ca.spottedleaf.moonrise.common.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())).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 5cb69d0b822e11a99a96aef4f59986d083b079f4..78f9c3f3b068f638570942961ad41969abc9c028 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) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot read world asynchronously");
|
|
+ }
|
|
+ // Folia end - parallel world ticking
|
|
return this.world.getBlockState(this.position);
|
|
}
|
|
|
|
@@ -157,6 +162,11 @@ public class CraftBlock implements Block {
|
|
}
|
|
|
|
private void setData(final byte data, int flag) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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);
|
|
}
|
|
|
|
@@ -198,6 +208,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) {
|
|
+ ca.spottedleaf.moonrise.common.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
|
|
@@ -343,18 +359,33 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public Biome getBiome() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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) {
|
|
+ ca.spottedleaf.moonrise.common.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) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio);
|
|
}
|
|
|
|
@@ -402,6 +433,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean isBlockFaceIndirectlyPowered(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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);
|
|
@@ -414,6 +450,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public int getBlockPower(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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();
|
|
@@ -484,6 +525,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean breakNaturally() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper end - parallel world ticking
|
|
return this.breakNaturally(null);
|
|
}
|
|
|
|
@@ -543,6 +589,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean applyBoneMeal(BlockFace face) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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();
|
|
@@ -554,8 +605,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;
|
|
@@ -644,6 +695,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) {
|
|
+ ca.spottedleaf.moonrise.common.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();
|
|
@@ -685,6 +741,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public boolean canPlace(BlockData data) {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.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();
|
|
@@ -719,6 +780,11 @@ public class CraftBlock implements Block {
|
|
|
|
@Override
|
|
public void tick() {
|
|
+ // SparklyPaper start - parallel world ticking
|
|
+ if (world instanceof ServerLevel serverWorld) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, position, "Cannot modify world asynchronously");
|
|
+ }
|
|
+ // SparklyPaper 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 04ae258a2f8e98421340d29d5cceedec045171b7..3e90f16d303a8f3d1e2b01e7cbd62cbd991d3f06 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
|
|
@@ -25,7 +25,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());
|
|
@@ -34,8 +34,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 fa63a6cfcfcc4eee4503a82d85333c139c8c8b2b..9856860ee2987738bbcad5d752670e30f569ba74 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
|
|
@@ -215,6 +215,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) {
|
|
+ ca.spottedleaf.moonrise.common.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) {
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(world.getHandle(), position, "Cannot modify world asynchronously"); // SparklyPaper - 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 56453454cbd4b9e9270fc833f8ab38d5fa7a3763..ad507c36d89ce8639fbedbb1df0da66bdc96affa 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
|
@@ -249,8 +249,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);
|
|
@@ -258,7 +258,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 bbd000e3a3e9112e10a09e5d3ef6851b28e900fa..6b91661e920b80fb2ddf2b3e9e2c2839a7986b01 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -951,7 +951,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
|
|
@@ -963,7 +963,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()) {
|
|
@@ -2248,7 +2248,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;
|
|
}
|