diff --git a/gradle.properties b/gradle.properties index b51e5cc..5951e5b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group=net.sparklypower.sparklypaper -version=1.21.8-R0.1-SNAPSHOT -mcVersion=1.21.8 -paperRef=29c8822d90899c89d2689338e81a98f690bcba12 +version=1.21.9-rc1-R0.1-SNAPSHOT +mcVersion=1.21.9-rc1 +paperRef=404e49fdc6493247278ccc263b43d2e253c7f42f org.gradle.configuration-cache=true org.gradle.caching=true diff --git a/sparklypaper-api/build.gradle.kts.patch b/sparklypaper-api/build.gradle.kts.patch index 0fb1d3d..79a755c 100644 --- a/sparklypaper-api/build.gradle.kts.patch +++ b/sparklypaper-api/build.gradle.kts.patch @@ -41,7 +41,7 @@ options.use() options.isDocFilesSubDirs = true options.links( -@@ -199,11 +_,11 @@ +@@ -201,11 +_,11 @@ } // workaround for https://github.com/gradle/gradle/issues/4046 diff --git a/sparklypaper-server/build.gradle.kts.patch b/sparklypaper-server/build.gradle.kts.patch index beb1240..fed5226 100644 --- a/sparklypaper-server/build.gradle.kts.patch +++ b/sparklypaper-server/build.gradle.kts.patch @@ -58,7 +58,7 @@ + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") + implementation("com.charleskorn.kaml:kaml:0.78.0") + // SparklyPaper end - implementation("ca.spottedleaf:concurrentutil:0.0.3") + implementation("ca.spottedleaf:concurrentutil:0.0.5") implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 @@ -195,9 +_,14 @@ diff --git a/sparklypaper-server/minecraft-patches/features/0001-Parallel-World-Ticking.patch b/sparklypaper-server/minecraft-patches/features/0001-Parallel-World-Ticking.patch deleted file mode 100644 index 86f5087..0000000 --- a/sparklypaper-server/minecraft-patches/features/0001-Parallel-World-Ticking.patch +++ /dev/null @@ -1,823 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MrPowerGamerBR -Date: Sun, 25 May 2025 21:39:32 -0300 -Subject: [PATCH] Parallel World Ticking - - -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -index 6ce4a98e4d3b633e3c87944c23b6b3f0ff58f159..9be4bbf155dae9ef34e423f5e84de8292c6e321c 100644 ---- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -@@ -1142,7 +1142,7 @@ public final class ChunkHolderManager { - if (changedFullStatus.isEmpty()) { - return; - } -- if (!TickThread.isTickThread()) { -+ if (!TickThread.isTickThreadFor(world)) { // SparklyPaper - parallel world ticking - // These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update - // which will invoke processTicketUpdates - this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus); -@@ -1163,7 +1163,7 @@ public final class ChunkHolderManager { - - // note: never call while inside the chunk system, this will absolutely break everything - public void processUnloads() { -- TickThread.ensureTickThread("Cannot unload chunks off-main"); -+ TickThread.ensureTickThread(world, "Cannot unload chunks off-main"); // SparklyPaper - parallel world ticking - - if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) { - throw new IllegalStateException("Cannot unload chunks recursively"); -@@ -1429,12 +1429,12 @@ public final class ChunkHolderManager { - if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) { - throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager"); - } -- final boolean isTickThread = TickThread.isTickThread(); -+ final boolean isTickThread = TickThread.isTickThreadFor(world); - - if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) { - TickThread.ensureTickThread("Cannot asynchronously process ticket updates"); - } -- -+ - boolean ret = false; - - if (this.ticketLevelPropagator.hasPendingUpdates()) { -diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java -index ca02c4c71a0a5a1a0ae8bbb40f0b1b7eac64e6fd..7ce68f270d809ab0b2be45ffdd0346f053bd3b1e 100644 ---- a/net/minecraft/core/dispenser/DispenseItemBehavior.java -+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java -@@ -401,8 +401,8 @@ public interface DispenseItemBehavior { - // CraftBukkit start - level.captureTreeGeneration = false; - if (!level.capturedBlockStates.isEmpty()) { -- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType; -- net.minecraft.world.level.block.SaplingBlock.treeType = null; -+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking -+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(null); // SparklyPaper - parallel world ticking - org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level.getWorld()); - List states = new java.util.ArrayList<>(level.capturedBlockStates.values()); - level.capturedBlockStates.clear(); -diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 7aac2a6889af3edaebfaf94deecbf00d00758b68..650b324eeb6173d51069a918ec2fd1e84e054c30 100644 ---- a/net/minecraft/server/MinecraftServer.java -+++ b/net/minecraft/server/MinecraftServer.java -@@ -303,6 +303,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping - public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation -+ public java.util.concurrent.Semaphore serverLevelTickingSemaphore = null; // SparklyPaper - parallel world ticking - - public static S spin(Function threadFunction) { - ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system -@@ -1704,6 +1705,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> tasks = new java.util.ArrayDeque<>(); -+ try { - for (ServerLevel serverLevel : this.getAllLevels()) { - serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent - serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent -@@ -1720,18 +1724,45 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { -+ try { -+ ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread currentThread = (ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread) Thread.currentThread(); -+ currentThread.currentlyTickingServerLevel = serverLevel; -+ -+ long i = Util.getNanos(); // SparklyPaper - track world's MSPT -+ serverLevel.tick(hasTimeLeft); -+ // SparklyPaper start - track world's MSPT -+ long j = Util.getNanos() - i; -+ -+ // These are from the "tickServer" function -+ serverLevel.tickTimes5s.add(this.tickCount, j); -+ serverLevel.tickTimes10s.add(this.tickCount, j); -+ serverLevel.tickTimes60s.add(this.tickCount, j); -+ // SparklyPaper end -+ } catch (Throwable throwable) { -+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world"); -+ serverLevel.fillReportDetails(crashreport); -+ throw new ReportedException(crashreport); -+ } finally { -+ serverLevelTickingSemaphore.release(); -+ } -+ }, serverLevel) -+ ); - - profilerFiller.pop(); - profilerFiller.pop(); - serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions - } -+ -+ while (!tasks.isEmpty()) { -+ tasks.pop().get(); -+ } -+ } catch (java.lang.InterruptedException | java.util.concurrent.ExecutionException e) { -+ throw new RuntimeException(e); // Propagate exception -+ } -+ // SparklyPaper end - this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked - - profilerFiller.popPush("connection"); -@@ -1822,6 +1853,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> oldLevels = this.levels; - Map, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); - newLevels.remove(level.dimension()); -+ level.tickExecutor.shutdown(); // SparklyPaper - parallel world ticking (We remove it in here instead of ServerLevel.close() because ServerLevel.close() is never called!) - this.levels = Collections.unmodifiableMap(newLevels); - } - // CraftBukkit end -diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java -index 17846055ae1b88a357ed671362aab7983331921a..36aae942ff6ea14a3e6060140af661a6692af891 100644 ---- a/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/net/minecraft/server/dedicated/DedicatedServer.java -@@ -193,6 +193,10 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - } - net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this); - // SparklyPaper end -+ // SparklyPaper start - parallel world ticking start -+ serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getConfig().getParallelWorldTicking().getThreads()); -+ DedicatedServer.LOGGER.info("Using " + serverLevelTickingSemaphore.availablePermits() + " permits for parallel world ticking"); // SparklyPaper - parallel world ticking -+ // SparklyPaper end - - this.setPvpAllowed(properties.pvp); - this.setFlightAllowed(properties.allowFlight); -diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 2882cd829d4d8e1f8615f085f6908efcdf68ac62..7b058fed0ca52b2f74f5cf4534df7b24c2da5c59 100644 ---- a/net/minecraft/server/level/ServerChunkCache.java -+++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -177,7 +177,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - - // call mid-tick tasks for chunk system - if ((i & 7) == 0) { -- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks(); -+ // ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - continue; - } - } -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index ecfe237fbecde610d095647a1f2a10f7d426d786..4211816c3b21a2ee66297a8f58b51de378bfd36c 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -182,7 +182,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - private final MinecraftServer server; - public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type - private int lastSpawnChunkRadius; -- final EntityTickList entityTickList = new EntityTickList(); -+ final EntityTickList entityTickList = new EntityTickList(this); // SparklyPaper - parallel world ticking - private final ServerWaypointManager waypointManager; - // Paper - rewrite chunk system - private final GameEventDispatcher gameEventDispatcher; -@@ -207,6 +207,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - private final StructureCheck structureCheck; - private final boolean tickTime; - private final RandomSequences randomSequences; -+ public java.util.concurrent.ExecutorService tickExecutor; // SparklyPaper - parallel world ticking - - // CraftBukkit start - public final LevelStorageSource.LevelStorageAccess levelStorageAccess; -@@ -665,7 +666,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.sleepStatus = new SleepStatus(); - this.gameEventDispatcher = new GameEventDispatcher(this); - this.randomSequences = Objects.requireNonNullElseGet(randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.TYPE)); -- this.waypointManager = new ServerWaypointManager(); -+ this.waypointManager = new ServerWaypointManager(this); // SparklyPaper - parallel world ticking - // Paper start - rewrite chunk system - this.moonrise$setEntityLookup(new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup((ServerLevel)(Object)this, ((ServerLevel)(Object)this).new EntityCallbacks())); - this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this); -@@ -681,6 +682,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.chunkDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.ChunkDataController((ServerLevel)(Object)this, this.chunkTaskScheduler); - // Paper end - rewrite chunk system - this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit -+ this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new net.sparklypower.sparklypaper.ServerLevelTickExecutorThreadFactory(getWorld().getName())); // SparklyPaper - parallel world ticking - } - - // Paper start -@@ -1233,7 +1235,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - // Paper start - rewrite chunk system - if ((++this.tickedBlocksOrFluids & 7L) != 0L) { -- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); -+ // ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - } - // Paper end - rewrite chunk system - -@@ -1246,7 +1248,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - // Paper start - rewrite chunk system - if ((++this.tickedBlocksOrFluids & 7L) != 0L) { -- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); -+ // ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - } - // Paper end - rewrite chunk system - -@@ -1520,6 +1522,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - } - - private void addPlayer(ServerPlayer player) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - Entity entity = this.getEntity(player.getUUID()); - if (entity != null) { - LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); -@@ -1532,7 +1535,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - - // CraftBukkit start - private boolean addEntity(Entity entity, @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { -- org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add entity off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process - // Paper start - extra debug info - if (entity.valid) { -diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 37c0a84758a1526791913898358659de202fa19f..a40e62beb26f89d306af3a5b6ce74b93407cde76 100644 ---- a/net/minecraft/server/level/ServerPlayer.java -+++ b/net/minecraft/server/level/ServerPlayer.java -@@ -459,6 +459,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - return this.viewDistanceHolder; - } - // Paper end - rewrite chunk system -+ public boolean hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (fixes bug in DreamResourceReset where the inventory is opened AFTER the player has changed worlds, if you click with the quick tp torch in a chest, because the inventory is opened AFTER the player has teleported) - - public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) { - super(level, gameProfile); -@@ -739,6 +740,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - - @Override - public void tick() { -+ hasTickedAtLeastOnceInNewWorld = true; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch) - // CraftBukkit start - if (this.joining) { - this.joining = false; -@@ -1403,6 +1405,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - return this; - } else { - // CraftBukkit start -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + serverLevel.getWorld().getName() + " to world " + level.getWorld().getName()); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - /* - this.isChangingDimension = true; - LevelData levelData = level.getLevelData(); -@@ -1739,6 +1742,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - return OptionalInt.empty(); - } else { - // CraftBukkit start -+ // SparklyPaper start - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch) -+ if (!hasTickedAtLeastOnceInNewWorld) { -+ MinecraftServer.LOGGER.warn("Ignoring request to open container " + abstractContainerMenu + " because we haven't ticked in the current world yet!", new Throwable()); -+ return OptionalInt.empty(); -+ } -+ // SparklyPaper end - this.containerMenu = abstractContainerMenu; // Moved up - if (!this.isImmobile()) - this.connection -@@ -1803,6 +1812,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - } - @Override - public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { -+ // SparklyPaper start - parallel world ticking (debugging) -+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) { -+ MinecraftServer.LOGGER.warn("Closing " + this.getBukkitEntity().getName() + " inventory that was created at", this.containerMenu.containerCreationStacktrace); -+ } -+ // SparklyPaper end - org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit - // Paper end - Inventory close reason - this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); -diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 2c2015bf19d5b30e945118661b7a9b474a1bddc8..816f850e636385ae61832874d9f46be86dda132c 100644 ---- a/net/minecraft/server/players/PlayerList.java -+++ b/net/minecraft/server/players/PlayerList.java -@@ -112,7 +112,7 @@ public abstract class PlayerList { - private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); - private final MinecraftServer server; - public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety -- private final Map playersByUUID = Maps.newHashMap(); -+ private final Map playersByUUID = Maps.newHashMap(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map) - private final UserBanList bans = new UserBanList(USERBANLIST_FILE); - private final IpBanList ipBans = new IpBanList(IPBANLIST_FILE); - private final ServerOpList ops = new ServerOpList(OPLIST_FILE); -@@ -133,7 +133,7 @@ public abstract class PlayerList { - - // CraftBukkit start - private org.bukkit.craftbukkit.CraftServer cserver; -- private final Map playersByName = new java.util.HashMap<>(); -+ private final Map playersByName = new java.util.HashMap<>(); // SparklyPaper - parallel world ticking (we don't need to replace the original map because we never iterate on top of this map) - public @Nullable String collideRuleTeamName; // Paper - Configurable player collision - - public PlayerList(MinecraftServer server, LayeredRegistryAccess registries, PlayerDataStorage playerIo, int maxPlayers) { -@@ -149,6 +149,7 @@ public abstract class PlayerList { - abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor - - public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - player.isRealPlayer = true; // Paper - player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed - GameProfile gameProfile = player.getGameProfile(); -@@ -676,6 +677,12 @@ public abstract class PlayerList { - } - - public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, @Nullable org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, @Nullable org.bukkit.Location location) { -+ // SparklyPaper - parallel world ticking (additional concurrency issues logs) -+ if (location != null) // TODO: Is this really never null, or is IntelliJ IDEA tripping? Because I'm pretty sure that this can be null and there isn't any @NotNull annotations -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, from world " + player.level().getWorld().getName() + " to world " + location.getWorld().getName()); -+ else -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, respawning in world " + player.level().getWorld().getName()); -+ // SparklyPaper end - player.stopRiding(); // CraftBukkit - this.players.remove(player); - this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot -@@ -686,6 +693,7 @@ public abstract class PlayerList { - ServerPlayer serverPlayer = player; - Level fromWorld = player.level(); - player.wonGame = false; -+ serverPlayer.hasTickedAtLeastOnceInNewWorld = false; // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch) - // CraftBukkit end - serverPlayer.connection = player.connection; - serverPlayer.restoreFrom(player, keepInventory); -diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java -index f9e7532f86122a379692561a639a209a126e8bba..2709dfae53c1a210f36c45fa0df0d49536341a38 100644 ---- a/net/minecraft/server/waypoints/ServerWaypointManager.java -+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java -@@ -20,8 +20,16 @@ public class ServerWaypointManager implements WaypointManager players = new HashSet<>(); - private final Table connections = HashBasedTable.create(); - -+ // SparklyPaper start - parallel world ticking -+ private final net.minecraft.server.level.ServerLevel level; -+ public ServerWaypointManager(net.minecraft.server.level.ServerLevel level) { -+ this.level = level; -+ } -+ // SparklyPaper end -+ - @Override - public void trackWaypoint(WaypointTransmitter waypoint) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot track waypoints off-main"); // SparklyPaper - parallel world ticking - this.waypoints.add(waypoint); - - for (ServerPlayer serverPlayer : this.players) { -@@ -31,6 +39,7 @@ public class ServerWaypointManager implements WaypointManager map = Tables.transpose(this.connections).row(waypoint); - SetView set = Sets.difference(this.players, map.keySet()); -@@ -47,12 +56,14 @@ public class ServerWaypointManager implements WaypointManager connection.disconnect()); - Tables.transpose(this.connections).row(waypoint).clear(); - this.waypoints.remove(waypoint); - } - - public void addPlayer(ServerPlayer player) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot add player to waypoints off-main"); // SparklyPaper - parallel world ticking - this.players.add(player); - - for (WaypointTransmitter waypointTransmitter : this.waypoints) { -@@ -65,6 +76,7 @@ public class ServerWaypointManager implements WaypointManager map = this.connections.row(player); - SetView set = Sets.difference(this.waypoints, map.keySet()); - -@@ -78,6 +90,7 @@ public class ServerWaypointManager implements WaypointManager { - connection.disconnect(); - return true; -@@ -87,6 +100,7 @@ public class ServerWaypointManager implements WaypointManager { -@@ -122,6 +137,7 @@ public class ServerWaypointManager implements WaypointManager passengers = this.getPassengers(); - List list = new ArrayList<>(passengers.size()); - this.ejectPassengers(); -diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java -index 96da9b8e3556e58365dc6742f44d6dfd608e2953..3366a21dd176efb09718a70fcb323c2e8d35e8a9 100644 ---- a/net/minecraft/world/inventory/AbstractContainerMenu.java -+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java -@@ -95,8 +95,14 @@ public abstract class AbstractContainerMenu { - - public void startOpen() {} - // CraftBukkit end -+ public Throwable containerCreationStacktrace; // SparklyPaper - parallel world ticking (debugging) - - protected AbstractContainerMenu(@Nullable MenuType menuType, int containerId) { -+ // SparklyPaper - parallel world ticking (debugging) -+ if (net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.getLogContainerCreationStacktraces()) { -+ this.containerCreationStacktrace = new Throwable(); -+ } -+ // SparklyPaper end - this.menuType = menuType; - this.containerId = containerId; - } -diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java -index da16f4831c875e07c25d7ed041bed493db614658..fc1512e434f5363d28f5cb9d58094c2d99bfb565 100644 ---- a/net/minecraft/world/item/ItemStack.java -+++ b/net/minecraft/world/item/ItemStack.java -@@ -398,8 +398,8 @@ public final class ItemStack implements DataComponentHolder { - if (interactionResult.consumesAction() && serverLevel.captureTreeGeneration && !serverLevel.capturedBlockStates.isEmpty()) { - serverLevel.captureTreeGeneration = false; - org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(clickedPos, serverLevel.getWorld()); -- org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType; -- net.minecraft.world.level.block.SaplingBlock.treeType = null; -+ org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking -+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(null); - List blocks = new java.util.ArrayList<>(serverLevel.capturedBlockStates.values()); - serverLevel.capturedBlockStates.clear(); - org.bukkit.event.world.StructureGrowEvent structureEvent = null; -diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index 722dc48c88e7a7374e7b9aa5bcdc0ed2cc1775b4..0184cf93144246d464ed1654a1850f1729460ce6 100644 ---- a/net/minecraft/world/level/Level.java -+++ b/net/minecraft/world/level/Level.java -@@ -161,6 +161,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - } - // Paper end - add paper world config - -+ public io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE); // SparklyPaper - parallel world ticking (moved to world) - public static @Nullable BlockPos lastPhysicsProblem; // Spigot - private int tileTickPosition; - public final Map explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions -@@ -1085,6 +1086,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - - @Override - public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - // CraftBukkit start - tree generation - if (this.captureTreeGeneration) { - // Paper start - Protect Bedrock and End Portal/Frames from being destroyed -@@ -1466,7 +1468,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - tickingBlockEntity.tick(); - // Paper start - rewrite chunk system - if ((++tickedEntities & 7) == 0) { -- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); -+ // ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - } - // Paper end - rewrite chunk system - } -@@ -1489,7 +1491,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); - // Paper end - Prevent block entity and entity crashes - } -- this.moonrise$midTickTasks(); // Paper - rewrite chunk system -+ // this.moonrise$midTickTasks(); // Paper - rewrite chunk system // SparklyPaper - parallel world ticking (only run mid-tick at the end of each tick / fixes concurrency bugs related to executeMidTickTasks) - } - - // Paper start - Option to prevent armor stands from doing entity lookups -@@ -1626,6 +1628,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - @Nullable - @Override - public BlockEntity getBlockEntity(BlockPos pos) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // SparklyPaper - parallel world ticking - // Paper start - Perf: Optimize capturedTileEntities lookup - net.minecraft.world.level.block.entity.BlockEntity blockEntity; - if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != null) { -@@ -1642,6 +1645,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - } - - public void setBlockEntity(BlockEntity blockEntity) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // SparklyPaper - parallel world ticking - BlockPos blockPos = blockEntity.getBlockPos(); - if (!this.isOutsideBuildHeight(blockPos)) { - // CraftBukkit start -@@ -1726,6 +1730,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - - @Override - public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - Profiler.get().incrementCounter("getEntities"); - List list = Lists.newArrayList(); - -@@ -2051,8 +2056,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl - public abstract RecipeAccess recipeAccess(); - - public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) { -- this.randValue = this.randValue * 3 + 1013904223; -- int i = this.randValue >> 2; -+ int i = this.random.nextInt() >> 2; // SparklyPaper - parallel world ticking - return new BlockPos(x + (i & 15), y + (i >> 16 & yMask), z + (i >> 8 & 15)); - } - -diff --git a/net/minecraft/world/level/block/FungusBlock.java b/net/minecraft/world/level/block/FungusBlock.java -index 9711efb088bd0da9168e9bcd0496bd7caddd2974..63da283d68c7e1184d1995aeb08cd8720f1b02ee 100644 ---- a/net/minecraft/world/level/block/FungusBlock.java -+++ b/net/minecraft/world/level/block/FungusBlock.java -@@ -76,9 +76,9 @@ public class FungusBlock extends VegetationBlock implements BonemealableBlock { - // CraftBukkit start - .map((value) -> { - if (this == Blocks.WARPED_FUNGUS) { -- SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS; -+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.WARPED_FUNGUS); // SparklyPaper - parallel world ticking - } else if (this == Blocks.CRIMSON_FUNGUS) { -- SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS; -+ SaplingBlock.treeTypeRT.set(org.bukkit.TreeType.CRIMSON_FUNGUS); // SparklyPaper - parallel world ticking - } - return value; - }) -diff --git a/net/minecraft/world/level/block/MushroomBlock.java b/net/minecraft/world/level/block/MushroomBlock.java -index d306f5f524dc64618df94c9783c2168dc561a5e3..72363832a60a380869b60b1ec4a352a1e11608a8 100644 ---- a/net/minecraft/world/level/block/MushroomBlock.java -+++ b/net/minecraft/world/level/block/MushroomBlock.java -@@ -93,7 +93,7 @@ public class MushroomBlock extends VegetationBlock implements BonemealableBlock - return false; - } else { - level.removeBlock(pos, false); -- SaplingBlock.treeType = (this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM; // CraftBukkit -+ SaplingBlock.treeTypeRT.set((this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM); // CraftBukkit // SparklyPaper - parallel world ticking - if (optional.get().value().place(level, level.getChunkSource().getGenerator(), random, pos)) { - return true; - } else { -diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 1943a6aad888647953e2d9dbbeedb0bd81c6f9df..2c4c57deead027fc78904e7e9bc8a2e213225960 100644 ---- a/net/minecraft/world/level/block/RedStoneWireBlock.java -+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -267,7 +267,7 @@ public class RedStoneWireBlock extends Block { - - // Paper start - Optimize redstone (Eigencraft) - // The bulk of the new functionality is found in RedstoneWireTurbo.java -- io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this); -+ // io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this); // SparklyPaper - parallel world ticking (moved to world) - - /* - * Modified version of pre-existing updateSurroundingRedstone, which is called from -@@ -283,7 +283,7 @@ public class RedStoneWireBlock extends Block { - if (orientation != null) { - source = pos.relative(orientation.getFront().getOpposite()); - } -- turbo.updateSurroundingRedstone(worldIn, pos, state, source); -+ worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source); // SparklyPaper - parallel world ticking - return; - } - updatePowerStrength(worldIn, pos, state, orientation, blockAdded); -@@ -311,7 +311,7 @@ public class RedStoneWireBlock extends Block { - // [Space Walker] suppress shape updates and emit those manually to - // bypass the new neighbor update stack. - if (level.setBlock(pos, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS)) { -- turbo.updateNeighborShapes(level, pos, state); -+ level.turbo.updateNeighborShapes(level, pos, state); // SparklyPaper - parallel world ticking - } - } - } -diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java -index a22cb810622e0ae97bc2a0d6390d026d9482b783..7934299d633524a9ef8c9d315a48c0f5bbe13b4c 100644 ---- a/net/minecraft/world/level/block/SaplingBlock.java -+++ b/net/minecraft/world/level/block/SaplingBlock.java -@@ -25,7 +25,7 @@ public class SaplingBlock extends VegetationBlock implements BonemealableBlock { - public static final IntegerProperty STAGE = BlockStateProperties.STAGE; - private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 12.0); - protected final TreeGrower treeGrower; -- public static @javax.annotation.Nullable org.bukkit.TreeType treeType; // CraftBukkit -+ public static final ThreadLocal treeTypeRT = new ThreadLocal<>(); // SparklyPaper - parallel world ticking (from Folia) - - @Override - public MapCodec codec() { -@@ -62,8 +62,8 @@ public class SaplingBlock extends VegetationBlock implements BonemealableBlock { - this.treeGrower.growTree(level, level.getChunkSource().getGenerator(), pos, state, random); - level.captureTreeGeneration = false; - if (!level.capturedBlockStates.isEmpty()) { -- org.bukkit.TreeType treeType = SaplingBlock.treeType; -- SaplingBlock.treeType = null; -+ org.bukkit.TreeType treeType = SaplingBlock.treeTypeRT.get(); // SparklyPaper - parallel world ticking -+ SaplingBlock.treeTypeRT.set(null); - org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level.getWorld()); - java.util.List blocks = new java.util.ArrayList<>(level.capturedBlockStates.values()); - level.capturedBlockStates.clear(); -diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java -index 5a094257a31f0500278a706a418e1697f8810ffb..061c0f2422e46bad70df3b21ccb9e10a9e13ccaa 100644 ---- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java -@@ -76,6 +76,12 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co - } - - public static boolean canUnlock(Player player, LockCode code, Component displayName, @Nullable BlockEntity blockEntity) { -+ // SparklyPaper - parallel world ticking (see: PARALLEL_NOTES.md - Opening an inventory after a world switch) -+ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != serverPlayer.level()) { -+ net.minecraft.server.MinecraftServer.LOGGER.warn("Player " + serverPlayer.getScoreboardName() + " (" + serverPlayer.getStringUUID() + ") attempted to open a BlockEntity @ " + blockEntity.getLevel().getWorld().getName() + " " + blockEntity.getBlockPos().getX() + ", " + blockEntity.getBlockPos().getY() + ", " + blockEntity.getBlockPos().getZ() + " while they were in a different world " + serverPlayer.level().getWorld().getName() + " than the block themselves!"); -+ return false; -+ } -+ // SparklyPaper end - if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != null && blockEntity.getLevel().getBlockEntity(blockEntity.getBlockPos()) == blockEntity) { - final org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()); - net.kyori.adventure.text.Component lockedMessage = net.kyori.adventure.text.Component.translatable("container.isLocked", io.papermc.paper.adventure.PaperAdventure.asAdventure(displayName)); -diff --git a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -index 0a94670dc20bb9c521b0395633eb100393895f6a..4ad9c47862a9791f72a18835a343bf0e962c14c8 100644 ---- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -@@ -43,9 +43,7 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi - // Paper end - Fix NPE in SculkBloomEvent world access - - public static void serverTick(Level level, BlockPos pos, BlockState state, SculkCatalystBlockEntity sculkCatalyst) { -- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = sculkCatalyst.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep. - sculkCatalyst.catalystListener.getSculkSpreader().updateCursors(level, pos, level.getRandom(), true); -- org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit - } - - @Override -diff --git a/net/minecraft/world/level/block/grower/TreeGrower.java b/net/minecraft/world/level/block/grower/TreeGrower.java -index d23f255de9208f42125fa358a9e8194c984fe4d3..54d64e77dc8c4bef25822d74a30be1a860cfec36 100644 ---- a/net/minecraft/world/level/block/grower/TreeGrower.java -+++ b/net/minecraft/world/level/block/grower/TreeGrower.java -@@ -203,55 +203,57 @@ public final class TreeGrower { - - // CraftBukkit start - private void setTreeType(Holder> feature) { -+ org.bukkit.TreeType treeType; // SparklyPaper - parallel world ticking - if (feature.is(TreeFeatures.OAK) || feature.is(TreeFeatures.OAK_BEES_005)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TREE; -+ treeType = org.bukkit.TreeType.TREE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.HUGE_RED_MUSHROOM)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_MUSHROOM; -+ treeType = org.bukkit.TreeType.RED_MUSHROOM; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.HUGE_BROWN_MUSHROOM)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BROWN_MUSHROOM; -+ treeType = org.bukkit.TreeType.BROWN_MUSHROOM; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.JUNGLE_TREE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.COCOA_TREE; -+ treeType = org.bukkit.TreeType.COCOA_TREE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.JUNGLE_TREE_NO_VINE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SMALL_JUNGLE; -+ treeType = org.bukkit.TreeType.SMALL_JUNGLE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.PINE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD; -+ treeType = org.bukkit.TreeType.TALL_REDWOOD; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.SPRUCE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD; -+ treeType = org.bukkit.TreeType.REDWOOD; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.ACACIA)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA; -+ treeType = org.bukkit.TreeType.ACACIA; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.BIRCH) || feature.is(TreeFeatures.BIRCH_BEES_005)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIRCH; -+ treeType = org.bukkit.TreeType.BIRCH; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.SUPER_BIRCH_BEES_0002)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_BIRCH; -+ treeType = org.bukkit.TreeType.TALL_BIRCH; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.SWAMP_OAK)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP; -+ treeType = org.bukkit.TreeType.SWAMP; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.FANCY_OAK) || feature.is(TreeFeatures.FANCY_OAK_BEES_005)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIG_TREE; -+ treeType = org.bukkit.TreeType.BIG_TREE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.JUNGLE_BUSH)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE_BUSH; -+ treeType = org.bukkit.TreeType.JUNGLE_BUSH; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.DARK_OAK)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.DARK_OAK; -+ treeType = org.bukkit.TreeType.DARK_OAK; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.MEGA_SPRUCE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_REDWOOD; -+ treeType = org.bukkit.TreeType.MEGA_REDWOOD; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.MEGA_PINE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_PINE; -+ treeType = org.bukkit.TreeType.MEGA_PINE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE; -+ treeType = org.bukkit.TreeType.JUNGLE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.AZALEA_TREE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA; -+ treeType = org.bukkit.TreeType.AZALEA; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.MANGROVE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE; -+ treeType = org.bukkit.TreeType.MANGROVE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.TALL_MANGROVE)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_MANGROVE; -+ treeType = org.bukkit.TreeType.TALL_MANGROVE; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.CHERRY) || feature.is(TreeFeatures.CHERRY_BEES_005)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.CHERRY; -+ treeType = org.bukkit.TreeType.CHERRY; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.PALE_OAK) || feature.is(TreeFeatures.PALE_OAK_BONEMEAL)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK; -+ treeType = org.bukkit.TreeType.PALE_OAK; // SparklyPaper - parallel world ticking - } else if (feature.is(TreeFeatures.PALE_OAK_CREAKING)) { -- net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK_CREAKING; -+ treeType = org.bukkit.TreeType.PALE_OAK_CREAKING; // SparklyPaper - parallel world ticking - } else { - throw new IllegalArgumentException("Unknown tree generator " + feature); - } -+ net.minecraft.world.level.block.SaplingBlock.treeTypeRT.set(treeType); // SparklyPaper - parallel world ticking - } - // CraftBukkit end - } -diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 75578e6ed7233a03d9b6cd3c6d3997f1c6148392..061218fc1fb0c4f4405eb4980ac17622c891aad6 100644 ---- a/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -360,6 +360,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - @Nullable - @Override - public BlockState setBlockState(BlockPos pos, BlockState state, int flags) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, pos, "Updating block asynchronously"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - int y = pos.getY(); - LevelChunkSection section = this.getSection(this.getSectionIndex(y)); - boolean hasOnlyAir = section.hasOnlyAir(); -diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java -index 423779a2b690f387a4f0bd07b97b50e0baefda76..24f9632e73d73c2ad68ebf30eb0e4cca7befae2d 100644 ---- a/net/minecraft/world/level/entity/EntityTickList.java -+++ b/net/minecraft/world/level/entity/EntityTickList.java -@@ -10,17 +10,26 @@ import net.minecraft.world.entity.Entity; - - public class EntityTickList { - private final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system -+ // SparklyPaper start - parallel world ticking -+ // Used to track async entity additions/removals/loops -+ private final net.minecraft.server.level.ServerLevel serverLevel; -+ public EntityTickList(net.minecraft.server.level.ServerLevel serverLevel) { -+ this.serverLevel = serverLevel; -+ } -+ // SparklyPaper end - - private void ensureActiveIsNotIterated() { - // Paper - rewrite chunk system - } - - public void add(Entity entity) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist addition"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs) - this.ensureActiveIsNotIterated(); - this.entities.add(entity); // Paper - rewrite chunk system - } - - public void remove(Entity entity) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist removal"); // Paper // SparklyPaper - parallel world ticking (additional concurrency issues logs) - this.ensureActiveIsNotIterated(); - this.entities.remove(entity); // Paper - rewrite chunk system - } -@@ -30,6 +39,7 @@ public class EntityTickList { - } - - public void forEach(Consumer entity) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverLevel, "Asynchronous entity ticklist iteration"); // SparklyPaper - parallel world ticking (additional concurrency issues logs) - // Paper start - rewrite chunk system - // To ensure nothing weird happens with dimension travelling, do not iterate over new entries... - // (by dfl iterator() is configured to not iterate over new entries) -diff --git a/net/minecraft/world/level/saveddata/maps/MapIndex.java b/net/minecraft/world/level/saveddata/maps/MapIndex.java -index 06025d79cc2297119b22224d777aca79f9d3d9c1..c4c6e28b58ec921ebc18b427e562fbaa51da65c8 100644 ---- a/net/minecraft/world/level/saveddata/maps/MapIndex.java -+++ b/net/minecraft/world/level/saveddata/maps/MapIndex.java -@@ -23,8 +23,10 @@ public class MapIndex extends SavedData { - } - - public MapId getNextMapId() { -+ synchronized (TYPE) { // SparklyPaper start - parallel world ticking - MapId mapId = new MapId(++this.lastMapId); - this.setDirty(); - return mapId; -+ } // SparklyPaper end - } - } diff --git a/sparklypaper-server/minecraft-patches/sources/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java.patch b/sparklypaper-server/minecraft-patches/sources/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java.patch index b73e1bb..74923ea 100644 --- a/sparklypaper-server/minecraft-patches/sources/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java.patch @@ -1,6 +1,6 @@ --- a/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java +++ b/ca/spottedleaf/dataconverter/minecraft/converters/itemstack/ConverterItemStackToDataComponents.java -@@ -1220,6 +_,7 @@ +@@ -1230,6 +_,7 @@ ret.setString("id", this.id); ret.setInt("count", this.count); if (!this.tag.isEmpty()) { diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java.patch index de8f0fb..456d87d 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java +++ b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java -@@ -22,7 +_,7 @@ - private static final double LIMIT = 3.9; +@@ -20,7 +_,7 @@ + ); private final int id; private final UUID uuid; - private final EntityType type; @@ -9,7 +9,7 @@ private final double x; private final double y; private final double z; -@@ -179,6 +_,32 @@ +@@ -161,6 +_,32 @@ public float getYHeadRot() { return Mth.unpackDegrees(this.yHeadRot); } diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index 2b07684..d61fe8c 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java -@@ -184,6 +_,15 @@ - this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark - com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics - com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now +@@ -260,6 +_,15 @@ + org.spigotmc.SpigotConfig.registerCommands(); + // Spigot end + io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc. + // SparklyPaper start - config files + try { + net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.init((java.io.File) options.valueOf("sparklypaper-settings")); @@ -13,6 +13,6 @@ + } + net.sparklypower.sparklypaper.SparklyPaperCommands.INSTANCE.registerCommands(this); + // SparklyPaper end - - this.setPvpAllowed(properties.pvp); - this.setFlightAllowed(properties.allowFlight); + // Paper start - initialize global and world-defaults configuration + this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess()); + this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch index 2fd4b17..3ed9f88 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -209,6 +_,7 @@ +@@ -197,6 +_,7 @@ if (this.entity.hasImpulse || this.trackDelta || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) { Vec3 deltaMovement = this.entity.getDeltaMovement(); @@ -8,8 +8,8 @@ double d = deltaMovement.distanceToSqr(this.lastSentMovement); if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) { this.lastSentMovement = deltaMovement; -@@ -226,6 +_,7 @@ - this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement)); +@@ -214,6 +_,7 @@ + this.synchronizer.sendToTrackingPlayers(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement)); } } + } // SparklyPaper end diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch index 800481e..57d0f94 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -565,6 +_,12 @@ +@@ -585,6 +_,12 @@ } // Paper end - chunk tick iteration diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 6c7040d..8000dd5 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -1,7 +1,269 @@ --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -622,6 +_,31 @@ - LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7)); +@@ -8,26 +_,10 @@ + import com.mojang.brigadier.StringReader; + import com.mojang.brigadier.suggestion.Suggestions; + import com.mojang.logging.LogUtils; +-import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; ++import io.papermc.paper.adventure.PaperAdventure; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +-import java.net.SocketAddress; +-import java.util.Collections; +-import java.util.EnumSet; +-import java.util.HashMap; +-import java.util.List; +-import java.util.Map; +-import java.util.Objects; +-import java.util.Optional; +-import java.util.Set; +-import java.util.concurrent.CancellationException; +-import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.TimeUnit; +-import java.util.function.BiFunction; +-import java.util.function.Consumer; +-import java.util.stream.Collectors; +-import java.util.stream.Stream; +-import javax.annotation.Nullable; + import net.minecraft.ChatFormatting; + import net.minecraft.Util; + import net.minecraft.advancements.AdvancementHolder; +@@ -36,11 +_,7 @@ + import net.minecraft.commands.CommandSourceStack; + import net.minecraft.commands.Commands; + import net.minecraft.commands.arguments.ArgumentSignatures; +-import net.minecraft.core.BlockPos; +-import net.minecraft.core.Direction; +-import net.minecraft.core.Holder; +-import net.minecraft.core.Registry; +-import net.minecraft.core.Vec3i; ++import net.minecraft.core.*; + import net.minecraft.core.component.DataComponents; + import net.minecraft.core.registries.Registries; + import net.minecraft.gametest.framework.GameTestInstance; +@@ -49,93 +_,13 @@ + import net.minecraft.network.DisconnectionDetails; + import net.minecraft.network.HashedStack; + import net.minecraft.network.TickablePacketListener; +-import net.minecraft.network.chat.ChatType; +-import net.minecraft.network.chat.Component; +-import net.minecraft.network.chat.LastSeenMessages; +-import net.minecraft.network.chat.LastSeenMessagesValidator; +-import net.minecraft.network.chat.MessageSignature; +-import net.minecraft.network.chat.MessageSignatureCache; +-import net.minecraft.network.chat.PlayerChatMessage; +-import net.minecraft.network.chat.RemoteChatSession; +-import net.minecraft.network.chat.SignableCommand; +-import net.minecraft.network.chat.SignedMessageBody; +-import net.minecraft.network.chat.SignedMessageChain; ++import net.minecraft.network.chat.*; + import net.minecraft.network.protocol.Packet; + import net.minecraft.network.protocol.PacketUtils; + import net.minecraft.network.protocol.common.ServerboundClientInformationPacket; + import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; + import net.minecraft.network.protocol.configuration.ConfigurationProtocols; +-import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket; +-import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; +-import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket; +-import net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket; +-import net.minecraft.network.protocol.game.ClientboundMoveVehiclePacket; +-import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket; +-import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; +-import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +-import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; +-import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; +-import net.minecraft.network.protocol.game.ClientboundStartConfigurationPacket; +-import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; +-import net.minecraft.network.protocol.game.ClientboundTagQueryPacket; +-import net.minecraft.network.protocol.game.ClientboundTestInstanceBlockStatus; +-import net.minecraft.network.protocol.game.GameProtocols; +-import net.minecraft.network.protocol.game.ServerGamePacketListener; +-import net.minecraft.network.protocol.game.ServerboundAcceptTeleportationPacket; +-import net.minecraft.network.protocol.game.ServerboundBlockEntityTagQueryPacket; +-import net.minecraft.network.protocol.game.ServerboundChangeDifficultyPacket; +-import net.minecraft.network.protocol.game.ServerboundChangeGameModePacket; +-import net.minecraft.network.protocol.game.ServerboundChatAckPacket; +-import net.minecraft.network.protocol.game.ServerboundChatCommandPacket; +-import net.minecraft.network.protocol.game.ServerboundChatCommandSignedPacket; +-import net.minecraft.network.protocol.game.ServerboundChatPacket; +-import net.minecraft.network.protocol.game.ServerboundChatSessionUpdatePacket; +-import net.minecraft.network.protocol.game.ServerboundChunkBatchReceivedPacket; +-import net.minecraft.network.protocol.game.ServerboundClientCommandPacket; +-import net.minecraft.network.protocol.game.ServerboundClientTickEndPacket; +-import net.minecraft.network.protocol.game.ServerboundCommandSuggestionPacket; +-import net.minecraft.network.protocol.game.ServerboundConfigurationAcknowledgedPacket; +-import net.minecraft.network.protocol.game.ServerboundContainerButtonClickPacket; +-import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; +-import net.minecraft.network.protocol.game.ServerboundContainerClosePacket; +-import net.minecraft.network.protocol.game.ServerboundContainerSlotStateChangedPacket; +-import net.minecraft.network.protocol.game.ServerboundDebugSubscriptionRequestPacket; +-import net.minecraft.network.protocol.game.ServerboundEditBookPacket; +-import net.minecraft.network.protocol.game.ServerboundEntityTagQueryPacket; +-import net.minecraft.network.protocol.game.ServerboundInteractPacket; +-import net.minecraft.network.protocol.game.ServerboundJigsawGeneratePacket; +-import net.minecraft.network.protocol.game.ServerboundLockDifficultyPacket; +-import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +-import net.minecraft.network.protocol.game.ServerboundMoveVehiclePacket; +-import net.minecraft.network.protocol.game.ServerboundPaddleBoatPacket; +-import net.minecraft.network.protocol.game.ServerboundPickItemFromBlockPacket; +-import net.minecraft.network.protocol.game.ServerboundPickItemFromEntityPacket; +-import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket; +-import net.minecraft.network.protocol.game.ServerboundPlayerAbilitiesPacket; +-import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; +-import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket; +-import net.minecraft.network.protocol.game.ServerboundPlayerInputPacket; +-import net.minecraft.network.protocol.game.ServerboundPlayerLoadedPacket; +-import net.minecraft.network.protocol.game.ServerboundRecipeBookChangeSettingsPacket; +-import net.minecraft.network.protocol.game.ServerboundRecipeBookSeenRecipePacket; +-import net.minecraft.network.protocol.game.ServerboundRenameItemPacket; +-import net.minecraft.network.protocol.game.ServerboundSeenAdvancementsPacket; +-import net.minecraft.network.protocol.game.ServerboundSelectBundleItemPacket; +-import net.minecraft.network.protocol.game.ServerboundSelectTradePacket; +-import net.minecraft.network.protocol.game.ServerboundSetBeaconPacket; +-import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket; +-import net.minecraft.network.protocol.game.ServerboundSetCommandBlockPacket; +-import net.minecraft.network.protocol.game.ServerboundSetCommandMinecartPacket; +-import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket; +-import net.minecraft.network.protocol.game.ServerboundSetJigsawBlockPacket; +-import net.minecraft.network.protocol.game.ServerboundSetStructureBlockPacket; +-import net.minecraft.network.protocol.game.ServerboundSetTestBlockPacket; +-import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket; +-import net.minecraft.network.protocol.game.ServerboundSwingPacket; +-import net.minecraft.network.protocol.game.ServerboundTeleportToEntityPacket; +-import net.minecraft.network.protocol.game.ServerboundTestInstanceBlockActionPacket; +-import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; +-import net.minecraft.network.protocol.game.ServerboundUseItemPacket; ++import net.minecraft.network.protocol.game.*; + import net.minecraft.network.protocol.ping.ClientboundPongResponsePacket; + import net.minecraft.network.protocol.ping.ServerboundPingRequestPacket; + import net.minecraft.resources.ResourceKey; +@@ -144,76 +_,35 @@ + import net.minecraft.server.commands.GameModeCommand; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.server.level.ServerPlayer; +-import net.minecraft.util.FutureChain; +-import net.minecraft.util.Mth; +-import net.minecraft.util.ProblemReporter; +-import net.minecraft.util.SignatureValidator; +-import net.minecraft.util.StringUtil; +-import net.minecraft.util.TickThrottler; ++import net.minecraft.util.*; + import net.minecraft.world.InteractionHand; + import net.minecraft.world.InteractionResult; + import net.minecraft.world.effect.MobEffects; +-import net.minecraft.world.entity.Entity; +-import net.minecraft.world.entity.ExperienceOrb; +-import net.minecraft.world.entity.HasCustomInventoryScreen; +-import net.minecraft.world.entity.LivingEntity; +-import net.minecraft.world.entity.MoverType; +-import net.minecraft.world.entity.PlayerRideableJumping; +-import net.minecraft.world.entity.PositionMoveRotation; +-import net.minecraft.world.entity.Relative; ++import net.minecraft.world.entity.*; + import net.minecraft.world.entity.item.ItemEntity; +-import net.minecraft.world.entity.player.ChatVisiblity; +-import net.minecraft.world.entity.player.Inventory; +-import net.minecraft.world.entity.player.Player; +-import net.minecraft.world.entity.player.PlayerModelPart; +-import net.minecraft.world.entity.player.ProfilePublicKey; ++import net.minecraft.world.entity.player.*; + import net.minecraft.world.entity.projectile.AbstractArrow; + import net.minecraft.world.entity.vehicle.AbstractBoat; +-import net.minecraft.world.inventory.AnvilMenu; +-import net.minecraft.world.inventory.BeaconMenu; +-import net.minecraft.world.inventory.CrafterMenu; +-import net.minecraft.world.inventory.MerchantMenu; +-import net.minecraft.world.inventory.RecipeBookMenu; +-import net.minecraft.world.item.BlockItem; +-import net.minecraft.world.item.BucketItem; +-import net.minecraft.world.item.Item; +-import net.minecraft.world.item.ItemStack; +-import net.minecraft.world.item.Items; ++import net.minecraft.world.inventory.*; ++import net.minecraft.world.item.*; + import net.minecraft.world.item.component.WritableBookContent; + import net.minecraft.world.item.component.WrittenBookContent; + import net.minecraft.world.item.crafting.RecipeHolder; + import net.minecraft.world.item.crafting.RecipeManager; +-import net.minecraft.world.level.BaseCommandBlock; +-import net.minecraft.world.level.GameRules; +-import net.minecraft.world.level.GameType; +-import net.minecraft.world.level.Level; +-import net.minecraft.world.level.LevelReader; ++import net.minecraft.world.level.*; + import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.CommandBlock; +-import net.minecraft.world.level.block.entity.BlockEntity; +-import net.minecraft.world.level.block.entity.CommandBlockEntity; +-import net.minecraft.world.level.block.entity.CrafterBlockEntity; +-import net.minecraft.world.level.block.entity.JigsawBlockEntity; +-import net.minecraft.world.level.block.entity.SignBlockEntity; +-import net.minecraft.world.level.block.entity.StructureBlockEntity; +-import net.minecraft.world.level.block.entity.TestBlockEntity; +-import net.minecraft.world.level.block.entity.TestInstanceBlockEntity; +-import net.minecraft.world.level.block.state.BlockBehaviour; ++import net.minecraft.world.level.block.entity.*; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.material.Fluids; + import net.minecraft.world.level.storage.TagValueOutput; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.BlockHitResult; ++import net.minecraft.world.phys.HitResult; + import net.minecraft.world.phys.Vec3; + import net.minecraft.world.phys.shapes.BooleanOp; + import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; +-import org.slf4j.Logger; +- +-// CraftBukkit start +-import io.papermc.paper.adventure.PaperAdventure; // Paper +-import net.minecraft.world.inventory.Slot; +-import net.minecraft.world.phys.HitResult; + import org.bukkit.Location; + import org.bukkit.NamespacedKey; + import org.bukkit.craftbukkit.entity.CraftEntity; +@@ -222,24 +_,23 @@ + import org.bukkit.craftbukkit.util.CraftLocation; + import org.bukkit.event.Event; + import org.bukkit.event.block.Action; ++import org.bukkit.event.inventory.*; + import org.bukkit.event.inventory.ClickType; +-import org.bukkit.event.inventory.CraftItemEvent; +-import org.bukkit.event.inventory.InventoryAction; +-import org.bukkit.event.inventory.InventoryClickEvent; +-import org.bukkit.event.inventory.InventoryCreativeEvent; + import org.bukkit.event.inventory.InventoryType.SlotType; +-import org.bukkit.event.inventory.SmithItemEvent; +-import org.bukkit.event.player.PlayerCommandPreprocessEvent; +-import org.bukkit.event.player.PlayerInputEvent; +-import org.bukkit.event.player.PlayerInteractAtEntityEvent; +-import org.bukkit.event.player.PlayerInteractEntityEvent; +-import org.bukkit.event.player.PlayerItemHeldEvent; +-import org.bukkit.event.player.PlayerMoveEvent; ++import org.bukkit.event.player.*; + import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; +-import org.bukkit.event.player.PlayerSwapHandItemsEvent; +-import org.bukkit.event.player.PlayerTeleportEvent; +-import org.bukkit.event.player.PlayerToggleFlightEvent; +-import org.bukkit.event.player.PlayerToggleSprintEvent; ++import org.slf4j.Logger; ++ ++import javax.annotation.Nullable; ++import java.net.SocketAddress; ++import java.util.*; ++import java.util.concurrent.CancellationException; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.TimeUnit; ++import java.util.function.BiFunction; ++import java.util.function.Consumer; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; + // CraftBukkit end + + public class ServerGamePacketListenerImpl +@@ -628,6 +_,31 @@ + LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7)); } + // SparklyPaper start - Add PlayerMoveControllableVehicleEvent @@ -28,11 +290,11 @@ + f1 = playerMoveControllableVehicleEvent.getTo().getPitch(); + } + // SparklyPaper end -+ ++ // Paper start - optimise out extra getCubes boolean teleportBack = flag1; if (!teleportBack) { -@@ -1516,7 +_,7 @@ +@@ -1521,7 +_,7 @@ d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above @@ -41,7 +303,7 @@ if (this.player.onGround() && !packet.isOnGround() && flag) { // Paper start - Add PlayerJumpEvent org.bukkit.entity.Player player = this.getCraftPlayer(); -@@ -1550,7 +_,37 @@ +@@ -1555,7 +_,37 @@ boolean flag1 = this.player.verticalCollisionBelow; this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); @@ -80,7 +342,7 @@ final boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { -@@ -1683,14 +_,14 @@ +@@ -1688,14 +_,14 @@ && this.noBlocksAround(this.player); this.player.level().getChunkSource().move(this.player); Vec3 vec3 = new Vec3(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z); @@ -98,7 +360,7 @@ || this.player.hasLandedInLiquid() || this.player.onClimbable() || this.player.isSpectator() -@@ -1705,7 +_,7 @@ +@@ -1710,7 +_,7 @@ this.lastGoodZ = this.player.getZ(); } else { this.internalTeleport(x, y, z, f, f1); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. @@ -107,7 +369,7 @@ this.player.removeLatestMovementRecording(); } } -@@ -3175,6 +_,21 @@ +@@ -3186,6 +_,21 @@ } else { event = new CraftItemEvent(recipe, inventory, type, slotNum, click, action); } diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch index 537eb60..ca084fb 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/Monster.java +++ b/net/minecraft/world/entity/monster/Monster.java -@@ -137,6 +_,12 @@ +@@ -133,6 +_,12 @@ @Override public ItemStack getProjectile(ItemStack shootable) { if (shootable.getItem() instanceof ProjectileWeaponItem) { diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch index f7577b4..5f25143 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -2145,6 +_,12 @@ +@@ -1925,6 +_,12 @@ if (!(shootable.getItem() instanceof ProjectileWeaponItem)) { return ItemStack.EMPTY; } else { diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch index 2f2715e..e25f2c7 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -106,7 +_,7 @@ - public static final int TICKS_PER_DAY = 24000; - public static final int MAX_ENTITY_SPAWN_Y = 20000000; - public static final int MIN_ENTITY_SPAWN_Y = -20000000; +@@ -111,7 +_,7 @@ + .add(new ExplosionParticleInfo(ParticleTypes.POOF, 0.5F, 1.0F)) + .add(new ExplosionParticleInfo(ParticleTypes.SMOKE, 1.0F, 1.0F)) + .build(); - public final List blockEntityTickers = Lists.newArrayList(); + public final net.sparklypower.sparklypaper.BlockEntityTickersList blockEntityTickers = new net.sparklypower.sparklypaper.BlockEntityTickersList(); // SparklyPaper - optimize block entity removals - protected final NeighborUpdater neighborUpdater; + protected final CollectingNeighborUpdater neighborUpdater; private final List pendingBlockEntityTickers = Lists.newArrayList(); private boolean tickingBlockEntities; -@@ -134,6 +_,7 @@ +@@ -139,6 +_,7 @@ // CraftBukkit start public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + public net.sparklypower.sparklypaper.configs.SparklyPaperConfig.SparklyPaperWorldConfig sparklyPaperConfig; // SparklyPaper - config files private final CraftWorld world; - public boolean pvpMode; + public net.kyori.adventure.util.TriState pvpMode = net.kyori.adventure.util.TriState.NOT_SET; public @Nullable org.bukkit.generator.ChunkGenerator generator; -@@ -840,6 +_,7 @@ +@@ -845,6 +_,7 @@ // Paper end - getblock optimisations - cache world height/sections this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config @@ -25,7 +25,7 @@ this.generator = generator; this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment); -@@ -1455,13 +_,11 @@ +@@ -1406,13 +_,11 @@ boolean runsNormally = this.tickRateManager().runsNormally(); int tickedEntities = 0; // Paper - rewrite chunk system @@ -40,7 +40,7 @@ } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { tickingBlockEntity.tick(); // Paper start - rewrite chunk system -@@ -1471,7 +_,7 @@ +@@ -1422,7 +_,7 @@ // Paper end - rewrite chunk system } } @@ -48,4 +48,4 @@ + this.blockEntityTickers.removeMarkedEntries(); // SparklyPaper - optimize block entity removals this.tickingBlockEntities = false; - profilerFiller.pop(); + this.spigotConfig.currentPrimedTnt = 0; // Spigot diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch index dc04a5d..33825af 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/Blocks.java +++ b/net/minecraft/world/level/block/Blocks.java -@@ -6887,6 +_,52 @@ +@@ -7060,6 +_,52 @@ .sound(SoundType.SWEET_BERRY_BUSH) .pushReaction(PushReaction.DESTROY) ); diff --git a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch index 4e8bb7b..1199222 100644 --- a/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch +++ b/sparklypaper-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java -@@ -423,6 +_,7 @@ +@@ -424,6 +_,7 @@ } else { Direction opposite = blockEntity.facing.getOpposite(); if (isFullContainer(attachedContainer, opposite)) { diff --git a/sparklypaper-server/paper-patches/features/0001-Parallel-World-Ticking.patch b/sparklypaper-server/paper-patches/features/0001-Parallel-World-Ticking.patch deleted file mode 100644 index 6d8ebe1..0000000 --- a/sparklypaper-server/paper-patches/features/0001-Parallel-World-Ticking.patch +++ /dev/null @@ -1,650 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MrPowerGamerBR -Date: Sun, 25 May 2025 21:40:06 -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..8fc5691d608359ce848b8caecdd55ddd4372abfa 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 - - private static String getThreadContext() { - return "thread=" + Thread.currentThread().getName(); -@@ -26,6 +27,7 @@ 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()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(reason); - } - } -@@ -33,8 +35,9 @@ public class TickThread extends Thread { - 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -42,8 +45,9 @@ 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -51,8 +55,9 @@ public class TickThread extends Thread { - public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) { - if (!isTickThreadFor(world, pos)) { - final String ex = "Thread failed main thread check: " + -- reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos; -+ reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos + " - " + getTickThreadInformation(world.getServer()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -60,8 +65,9 @@ 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -69,8 +75,9 @@ 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -78,8 +85,9 @@ 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - throw new IllegalStateException(ex); - } - } -@@ -87,12 +95,75 @@ 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()); - LOGGER.error(ex, new Throwable()); -+ if (HARD_THROW) // SparklyPaper - parallel world ticking - 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(); -@@ -127,7 +198,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) { -@@ -135,38 +210,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 aa0a61d7f3128543b698647663fa4a9af2a8255a..0faa70d74dee3dd093b77de48dfb6c3564ff3578 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -479,7 +479,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; - } -@@ -496,6 +496,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; - -@@ -546,6 +547,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - - @Override - public boolean loadChunk(int x, int z, boolean generate) { -+ 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) - org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot - 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 -@@ -774,6 +776,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); -@@ -889,6 +892,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 -+ 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 -@@ -980,6 +984,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); -@@ -1010,6 +1015,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); -+ 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); - -@@ -2318,6 +2324,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 6809f9dcf8510c714145d99d250eb69f98d9bf27..ee04049faf283ac929b6b0806eaed430a7c57db8 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() { -+ // 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.world.getBlockState(this.position); - } - -@@ -155,6 +160,11 @@ public class CraftBlock implements Block { - } - - private void setData(final byte data, int flags) { -+ // 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), 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) { -+ // SparklyPaper start - parallel world ticking -+ if (world instanceof ServerLevel serverWorld) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, pos, "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 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() { -+ // 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); - } - -@@ -376,6 +406,11 @@ public class CraftBlock implements Block { - - @Override - public boolean isBlockIndirectlyPowered() { -+ // 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.world.getMinecraftWorld().hasNeighborSignal(this.position); - } - -@@ -415,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(); -@@ -504,6 +544,11 @@ public class CraftBlock implements Block { - - @Override - public boolean breakNaturally(ItemStack item, boolean triggerEffect, boolean dropExperience, boolean forceEffect) { -+ // 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 - // Paper end - // Order matters here, need to drop before setting to air so skulls can get their data - net.minecraft.world.level.block.state.BlockState state = this.getNMS(); -@@ -548,6 +593,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(); -@@ -559,8 +609,8 @@ public class CraftBlock implements Block { - world.captureTreeGeneration = false; - - if (!world.capturedBlockStates.isEmpty()) { -- 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 states = new ArrayList<>(world.capturedBlockStates.values()); - world.capturedBlockStates.clear(); - StructureGrowEvent structureEvent = null; -@@ -599,6 +649,11 @@ public class CraftBlock implements Block { - @Override - public Collection getDrops(ItemStack item, Entity entity) { - net.minecraft.world.level.block.state.BlockState state = this.getNMS(); -+ // 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 - net.minecraft.world.item.ItemStack nms = CraftItemStack.asNMSCopy(item); - - // Modelled off Player#hasCorrectToolForDrops -@@ -650,6 +705,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(); -@@ -691,6 +751,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(); -@@ -730,6 +795,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); - } -@@ -737,6 +807,11 @@ public class CraftBlock implements Block { - - @Override - public void fluidTick() { -+ // SparklyPaper start - parallel world ticking -+ if (this.world instanceof ServerLevel serverWorld) { -+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverWorld, this.position, "Cannot read world asynchronously"); -+ } -+ // SparklyPaper end - parallel world ticking - this.getNMSFluid().tick(this.world.getMinecraftWorld(), this.position, this.getNMS()); - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java -index 5d4faad9df4824cfd61abfd4df011c006f114424..d1227c670db2dbe1816d78be51796c43e99a4d9b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java -+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java -@@ -32,7 +32,7 @@ public abstract class CraftBlockEntityState extends Craft - private final T blockEntity; - private final T snapshot; - public boolean snapshotDisabled; // Paper -- public static boolean DISABLE_SNAPSHOT = false; // Paper -+ public static ThreadLocal DISABLE_SNAPSHOT = ThreadLocal.withInitial(() -> Boolean.FALSE); // SparklyPaper - parallel world ticking - - public CraftBlockEntityState(World world, T blockEntity) { - super(world, blockEntity.getBlockPos(), blockEntity.getBlockState()); -@@ -41,8 +41,8 @@ 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) { -+ this.snapshotDisabled = DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking -+ if (DISABLE_SNAPSHOT.get()) { // SparklyPaper - 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..641adf9666fef4d15bc9b585aabfe687a83fbe86 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(); - -+ // 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; -@@ -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) { -+ 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 3c9131ac3cfb4f758e76830ca48813feb321e4c6..a74bf90de9059c81802f617ddea07a30aea47d1e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java -+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java -@@ -195,14 +195,14 @@ 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; -+ boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT.get(); // SparklyPaper - parallel world ticking -+ CraftBlockEntityState.DISABLE_SNAPSHOT.set(!useSnapshot); // SparklyPaper - 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.DISABLE_SNAPSHOT.set(prev); // SparklyPaper - parallel world ticking - } - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index 807a8250913cafa685ad8928a34db5a4856e4e9c..b53f0aeb52a205ec7fa844623d5169148d0964fd 100644 ---- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -819,7 +819,7 @@ public class CraftEventFactory { - return false; - } - -- public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPos up to five methods deep. -+ public static final ThreadLocal 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 state, int flags) { - return handleBlockSpreadEvent(world, source, target, state, flags, false); -@@ -835,7 +835,7 @@ public class CraftEventFactory { - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, target); - snapshot.setData(state); - -- BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), snapshot); -+ BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverrideRT.get() != null ? CraftEventFactory.sourceBlockOverrideRT.get() : source), snapshot); - if (event.callEvent()) { - boolean result = snapshot.place(flags); - return !checkSetResult || result; diff --git a/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch b/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch index b5e7ffd..83daad8 100644 --- a/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch +++ b/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -1,14 +1,14 @@ --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1048,6 +_,7 @@ +@@ -992,6 +_,7 @@ org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); + net.sparklypower.sparklypaper.configs.SparklyPaperConfigUtils.INSTANCE.init((File) console.options.valueOf("sparklypaper-settings")); // SparklyPaper - config files for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty - world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -1063,6 +_,7 @@ + world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && world.getGameRules().getBoolean(GameRules.RULE_SPAWN_MONSTERS)); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) +@@ -1007,6 +_,7 @@ } } world.spigotConfig.init(); // Spigot @@ -16,7 +16,7 @@ } Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper -@@ -1080,6 +_,7 @@ +@@ -1024,6 +_,7 @@ org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper this.spark.registerCommandBeforePlugins(this); // Paper - spark diff --git a/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch b/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch index 587b190..88b6a88 100644 --- a/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch +++ b/sparklypaper-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch @@ -1,6 +1,6 @@ --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -1318,6 +_,23 @@ +@@ -1306,6 +_,23 @@ Bukkit.getPluginManager().callEvent(crafterCraftEvent); return crafterCraftEvent; } diff --git a/sparklypaper-server/paper-patches/files/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java.patch b/sparklypaper-server/paper-patches/files/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java.patch index 3a9ac1e..d07c73c 100644 --- a/sparklypaper-server/paper-patches/files/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java.patch +++ b/sparklypaper-server/paper-patches/files/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java.patch @@ -1,9 +1,9 @@ --- a/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java +++ b/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java -@@ -143,6 +_,9 @@ - Material.MUSIC_DISC_TEARS, - // 1.21.7 - Material.MUSIC_DISC_LAVA_CHICKEN, +@@ -153,6 +_,9 @@ + Material.WAXED_OXIDIZED_COPPER_BARS, Material.WAXED_OXIDIZED_COPPER_CHAIN, Material.WAXED_OXIDIZED_COPPER_CHEST, Material.WAXED_OXIDIZED_COPPER_GOLEM_STATUE, Material.WAXED_OXIDIZED_COPPER_LANTERN, Material.WAXED_OXIDIZED_LIGHTNING_ROD, Material.WAXED_WEATHERED_COPPER_BARS, + Material.WAXED_WEATHERED_COPPER_CHAIN, Material.WAXED_WEATHERED_COPPER_CHEST, Material.WAXED_WEATHERED_COPPER_GOLEM_STATUE, Material.WAXED_WEATHERED_COPPER_LANTERN, Material.WAXED_WEATHERED_LIGHTNING_ROD, Material.WEATHERED_COPPER_BARS, Material.WEATHERED_COPPER_CHAIN, + Material.WEATHERED_COPPER_CHEST, Material.WEATHERED_COPPER_GOLEM_STATUE, Material.WEATHERED_COPPER_LANTERN, Material.WEATHERED_LIGHTNING_ROD, + // SparklyPower custom blocks + Material.SPARKLYPOWER_RAINBOW_WOOL, Material.SPARKLYPOWER_RAINBOW_CONCRETE, Material.SPARKLYPOWER_RAINBOW_TERRACOTTA, Material.SPARKLYPOWER_ASPHALT_PLAYER, Material.SPARKLYPOWER_ASPHALT_SERVER, Material.SPARKLYPOWER_ASPHALT_PLAYER_SLAB, + Material.SPARKLYPOWER_ASPHALT_SERVER_SLAB,