From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Wed, 29 Jan 2025 00:59:03 +0300 Subject: [PATCH] SparklyPaper: Parallel world ticking Original project: https://github.com/SparklyPower/SparklyPaper diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java index 467065e3b40df17f38716499259b46663c174fd0..18822bed7986348bbbed1712db7dac65884d39a9 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java @@ -1129,7 +1129,7 @@ public final class ChunkHolderManager { if (changedFullStatus.isEmpty()) { return; } - if (!TickThread.isTickThread()) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && !TickThread.isTickThreadFor(world) || !TickThread.isTickThread()) { // DivineMC - 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); @@ -1150,7 +1150,13 @@ 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"); + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + TickThread.ensureTickThread(world, "Cannot unload chunks off-main"); + } else { + TickThread.ensureTickThread("Cannot unload chunks off-main"); + } + // DivineMC end - Parallel world ticking if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) { throw new IllegalStateException("Cannot unload chunks recursively"); @@ -1416,7 +1422,11 @@ 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(); + // DivineMC start - Parallel world ticking + final boolean isTickThread = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking + ? TickThread.isTickThreadFor(world) + : TickThread.isTickThread(); + // DivineMC end - Parallel world ticking if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) { TickThread.ensureTickThread("Cannot asynchronously process ticket updates"); diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java index 226088405c019922085285ba5d04d7c131470c69..5d9089d98ed0daa8ee680123e50c3ce9abd2bca6 100644 --- a/io/papermc/paper/entity/activation/ActivationRange.java +++ b/io/papermc/paper/entity/activation/ActivationRange.java @@ -120,7 +120,7 @@ public final class ActivationRange { * * @param world */ - public static void activateEntities(final Level world) { + public synchronized static void activateEntities(final Level world) { // DivineMC - Parallel world ticking final int miscActivationRange = world.spigotConfig.miscActivationRange; final int raiderActivationRange = world.spigotConfig.raiderActivationRange; final int animalActivationRange = world.spigotConfig.animalActivationRange; diff --git a/io/papermc/paper/redstone/RedstoneWireTurbo.java b/io/papermc/paper/redstone/RedstoneWireTurbo.java index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..4970243433e7de4ec2e452f25f8737cd57f2d263 100644 --- a/io/papermc/paper/redstone/RedstoneWireTurbo.java +++ b/io/papermc/paper/redstone/RedstoneWireTurbo.java @@ -829,14 +829,21 @@ public final class RedstoneWireTurbo { j = getMaxCurrentStrength(upd, j); int l = 0; - wire.shouldSignal = false; - // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated, - // and I'm not ready to try to replicate even more functionality from - // elsewhere in Minecraft into this accelerator. So sadly, we must - // suffer the performance hit of this very expensive call. If there - // is consistency to what this call returns, we may be able to cache it. - final int k = worldIn.getBestNeighborSignal(upd.self); - wire.shouldSignal = true; + // DivineMC start - Parallel world ticking + final int k; + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + k = wire.getBlockSignal(worldIn, upd.self); + } else { + wire.shouldSignal = false; + // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated, + // and I'm not ready to try to replicate even more functionality from + // elsewhere in Minecraft into this accelerator. So sadly, we must + // suffer the performance hit of this very expensive call. If there + // is consistency to what this call returns, we may be able to cache it. + k = worldIn.getBestNeighborSignal(upd.self); + wire.shouldSignal = true; + } + // DivineMC end - Parallel world ticking // The variable 'k' holds the maximum redstone power value of any adjacent blocks. // If 'k' has the highest level of all neighbors, then the power level of this diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java index 077ccddb7d358be352b6d497ed8b4e97b7d09262..7197434fdd304d0608b1d0bf2b1d6c6e2eb3d1df 100644 --- a/net/minecraft/core/dispenser/DispenseItemBehavior.java +++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java @@ -412,8 +412,10 @@ 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; + // DivineMC start - Parallel world ticking + org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.getTreeTypeTL(); + net.minecraft.world.level.block.SaplingBlock.setTreeTypeTL(null); + // DivineMC end - Parallel world ticking org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level); 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 76fb33ddc89c699db5d0e423106f5bbbee17c958..c4c4c0c76a1763fa4c7dadffc89376f796b2d93b 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -289,6 +289,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop serverPlayer1.connection.suspendFlushing()); this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit @@ -1760,28 +1785,43 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - BlockPhysicsEvent - serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent - serverLevel.updateLagCompensationTick(); // Paper - lag compensation - net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers - serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables - /* Drop global time updates - if (this.tickCount % 20 == 0) { - this.synchronizeTime(serverLevel); - } - // CraftBukkit end */ + // DivineMC start - Parallel world ticking + java.util.ArrayDeque> 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 + serverLevel.updateLagCompensationTick(); // Paper - lag compensation + net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers + serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + serverLevelTickingSemaphore.acquire(); + tasks.add( + serverLevel.tickExecutor.submit(() -> { + ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread currentThread = (ca.spottedleaf.moonrise.common.util.TickThread.ServerLevelTickThread) Thread.currentThread(); + currentThread.currentlyTickingServerLevel = serverLevel; + + try { + tickLevel(serverLevel, hasTimeLeft); + } finally { + serverLevelTickingSemaphore.release(); + } + }, serverLevel) + ); + } else { + tickLevel(serverLevel, hasTimeLeft); + } - try { - serverLevel.tick(hasTimeLeft); - } catch (Throwable var7) { - CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world"); - serverLevel.fillReportDetails(crashReport); - throw new ReportedException(crashReport); + serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions } - 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); } + // DivineMC end - Parallel world ticking this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked this.tickConnection(); @@ -1869,6 +1909,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop, ServerLevel> oldLevels = this.levels; Map, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels); newLevels.remove(level.dimension()); + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) level.tickExecutor.shutdown(); // DivineMC - Parallel world ticking this.levels = Collections.unmodifiableMap(newLevels); } // CraftBukkit end diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java index 3836d60ce84fb26f30a609486a5755d3fd1c94f1..637531fad0d4eb230dc078c1bce201d57d13f9ef 100644 --- a/net/minecraft/server/PlayerAdvancements.java +++ b/net/minecraft/server/PlayerAdvancements.java @@ -53,7 +53,7 @@ public class PlayerAdvancements { private AdvancementTree tree; private final Map progress = new LinkedHashMap<>(); private final Set visible = new HashSet<>(); - private final Set progressChanged = new HashSet<>(); + private final Set progressChanged = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? java.util.concurrent.ConcurrentHashMap.newKeySet() : new HashSet<>(); // DivineMC - Parallel world ticking private final Set rootsToUpdate = new HashSet<>(); private ServerPlayer player; @Nullable @@ -266,6 +266,7 @@ public class PlayerAdvancements { } public void flushDirty(ServerPlayer player, boolean showAdvancements) { + final boolean isConcurrent = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking; // DivineMC - Parallel world ticking if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) { Map map = new HashMap<>(); Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. @@ -277,13 +278,23 @@ public class PlayerAdvancements { this.rootsToUpdate.clear(); - for (AdvancementHolder advancementHolder : this.progressChanged) { - if (this.visible.contains(advancementHolder)) { - map.put(advancementHolder.id(), this.progress.get(advancementHolder)); + // DivineMC start - Parallel world ticking + if (!this.progressChanged.isEmpty()) { + Set toProcess = isConcurrent ? new HashSet<>(this.progressChanged) : this.progressChanged; + + for (AdvancementHolder advancementHolder : toProcess) { + if (this.visible.contains(advancementHolder)) { + map.put(advancementHolder.id(), this.progress.get(advancementHolder)); + } } - } - this.progressChanged.clear(); + if (isConcurrent) { + this.progressChanged.removeAll(toProcess); + } else { + this.progressChanged.clear(); + } + } + // DivineMC end - Parallel world ticking if (!map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { player.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set, set1, map, showAdvancements)); } diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java index c37851095cfe637a2768de0aa179efe66e9a4cde..1afd48654a421f0e0d4d5c2f27bdfe0b5f436bb8 100644 --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java @@ -311,6 +311,13 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface } // DivineMC end - Pufferfish: SIMD Support + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + serverLevelTickingSemaphore = new java.util.concurrent.Semaphore(org.bxteam.divinemc.config.DivineConfig.AsyncCategory.parallelThreadCount); + DedicatedServer.LOGGER.info("Using {} permits for Parallel world ticking", serverLevelTickingSemaphore.availablePermits()); + } + // DivineMC end - Parallel world ticking + // this.worldData.setGameType(properties.gameMode.get()); // CraftBukkit - moved to world loading LOGGER.info("Default game type: {}", properties.gameMode.get()); // Paper start - Unix domain socket support diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java index c5b95557dc148cfc91c031bf0789001868a60cbd..6ea62fbffda38e477ef8e119608fc93793db95c3 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java @@ -176,8 +176,12 @@ 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(); - continue; + // DivineMC start - Parallel world ticking + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer) this.level.getServer()).moonrise$executeMidTickTasks(); + continue; + } + // DivineMC end - Parallel world ticking } } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index e17060524fee0751042aa835e3d7fa04f5dd9b1d..a6d7afbca70e9c7df14df2f7f74788a1326be98e 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -186,7 +186,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public final ServerChunkCache chunkSource; private final MinecraftServer server; public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type - final EntityTickList entityTickList = new EntityTickList(); + final EntityTickList entityTickList = new EntityTickList(this); // DivineMC - Parallel world ticking private final ServerWaypointManager waypointManager; // Paper - rewrite chunk system private final GameEventDispatcher gameEventDispatcher; @@ -225,6 +225,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public boolean hasRidableMoveEvent = false; // Purpur - Ridables public net.minecraft.world.item.ItemStack ominousBanner; // DivineMC - Optimize Raids public org.bxteam.divinemc.util.tps.TPSCalculator tpsCalculator = new org.bxteam.divinemc.util.tps.TPSCalculator(); // DivineMC - Lag Compensation + public java.util.concurrent.ExecutorService tickExecutor; // DivineMC - Parallel world ticking @Override public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { @@ -704,7 +705,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); // DivineMC - 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); @@ -722,6 +723,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle this.ominousBanner = Objects.requireNonNullElse(this.registryAccess(), net.minecraft.core.RegistryAccess.EMPTY).lookup(Registries.BANNER_PATTERN).map(Raid::getOminousBannerInstance).orElse(null); // DivineMC - Optimize Raids + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.bxteam.divinemc.async.world.ServerLevelTickThreadFactory(getWorld().getName())); + } else { + this.tickExecutor = null; + } + // DivineMC end - Parallel world ticking } // Paper start @@ -763,6 +771,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.advanceWeatherCycle(); } + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && (++this.tickedBlocksOrFluids & 7L) != 0L) { + this.moonrise$midTickTasks(); + } + // DivineMC end - Parallel world ticking + int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) { // Purpur - Config for skipping night // Paper start - create time skip event - move up calculations @@ -1336,12 +1350,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe if (fluidState.is(fluid)) { fluidState.tick(this, pos, blockState); } - // Paper start - rewrite chunk system - if ((++this.tickedBlocksOrFluids & 7L) != 0L) { - ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + // DivineMC start - Parallel world ticking + ++this.tickedBlocksOrFluids; + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && (this.tickedBlocksOrFluids & 7L) != 0L) { + this.server.moonrise$executeMidTickTasks(); } - // Paper end - rewrite chunk system - + // DivineMC end - Parallel world ticking } private void tickBlock(BlockPos pos, Block block) { @@ -1349,12 +1363,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe if (blockState.is(block)) { blockState.tick(this, pos, this.random); } - // Paper start - rewrite chunk system - if ((++this.tickedBlocksOrFluids & 7L) != 0L) { - ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + // DivineMC start - Parallel world ticking + ++this.tickedBlocksOrFluids; + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && (this.tickedBlocksOrFluids & 7L) != 0L) { + this.server.moonrise$executeMidTickTasks(); } - // Paper end - rewrite chunk system - + // DivineMC end - Parallel world ticking } // Paper start - log detailed entity tick information @@ -1614,6 +1628,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } private void addPlayer(ServerPlayer player) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add player off-main"); // DivineMC - Parallel world ticking Entity entity = this.getEntity(player.getUUID()); if (entity != null) { LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); @@ -1626,7 +1641,13 @@ 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 + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this, "Cannot add entity off-main"); + } else { + org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot + } + // DivineMC end - Parallel world ticking 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 b1667ed08c417daf292e092ca0d2ab9848ac8d61..f7603988f9d818bd66eba8a9d3d3edbef347b6c2 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -444,6 +444,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc private boolean tpsBar = false; // Purpur - Implement TPSBar private boolean compassBar = false; // Purpur - Add compass command private boolean ramBar = false; // Purpur - Implement rambar commands + public boolean hasTickedAtLeastOnceInNewWorld = false; // DivineMC - Parallel world ticking // Paper start - rewrite chunk system private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; @@ -724,6 +725,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc @Override public void tick() { + hasTickedAtLeastOnceInNewWorld = true; // DivineMC - Parallel world ticking // CraftBukkit start if (this.joining) { this.joining = false; @@ -1558,6 +1560,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc teleportTransition.postTeleportTransition().onTransition(this); return this; } else { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot change dimension of a player off-main, from world " + level().getWorld().getName() + " to world " + level.getWorld().getName()); // DivineMC - Parallel world ticking (additional concurrency issues logs) this.isChangingDimension = true; LevelData levelData = level.getLevelData(); this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(level), ClientboundRespawnPacket.KEEP_ALL_DATA)); @@ -1874,6 +1877,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc return OptionalInt.empty(); } else { // CraftBukkit start + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && !hasTickedAtLeastOnceInNewWorld) { + MinecraftServer.LOGGER.warn("Ignoring request to open container {} because we haven't ticked in the current world yet!", abstractContainerMenu, new Throwable()); + return OptionalInt.empty(); + } + // DivineMC end - Parallel world ticking this.containerMenu = abstractContainerMenu; // Moved up if (!this.isImmobile()) this.connection @@ -1938,6 +1947,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc } @Override public void closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + // DivineMC start - Parallel world ticking (debugging) + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.logContainerCreationStacktraces) { + MinecraftServer.LOGGER.warn("Closing {} inventory that was created at", this.getBukkitEntity().getName(), this.containerMenu.containerCreationStacktrace); + } + // DivineMC end - Parallel world ticking (debugging) 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 42af746efa840152dfb0a8d73d9d227115254acc..9acc247ec540449a0e4cf3a5a0bc67a0e3dfbf15 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -150,6 +150,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) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot place new player off-main"); // DivineMC - Parallel world ticking player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed NameAndId nameAndId = player.nameAndId(); @@ -597,6 +598,7 @@ public abstract class PlayerList { // Paper start - respawn event public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureOnlyTickThread("Cannot respawn player off-main, respawning in world " + player.level().getWorld().getName()); // DivineMC - Parallel world ticking ServerPlayer.RespawnResult result = player.findRespawnPositionAndUseSpawnBlock0(!keepInventory, TeleportTransition.DO_NOTHING, respawnReason); if (result == null) { // disconnected player during the respawn event return player; @@ -609,6 +611,7 @@ public abstract class PlayerList { player.level().removePlayerImmediately(player, reason); ServerLevel level = teleportTransition.newLevel(); ServerPlayer serverPlayer = player; // Paper - TODO - recreate instance + serverPlayer.hasTickedAtLeastOnceInNewWorld = false; // DivineMC - Parallel world ticking serverPlayer.connection = player.connection; serverPlayer.restoreFrom(player, keepInventory); serverPlayer.setId(player.getId()); diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java index f9e7532f86122a379692561a639a209a126e8bba..839f6b7696ef85314da185bedba7cfc5870c314a 100644 --- a/net/minecraft/server/waypoints/ServerWaypointManager.java +++ b/net/minecraft/server/waypoints/ServerWaypointManager.java @@ -19,9 +19,17 @@ public class ServerWaypointManager implements WaypointManager waypoints = new HashSet<>(); private final Set players = new HashSet<>(); private final Table connections = HashBasedTable.create(); + // DivineMC start - Parallel world ticking + private final net.minecraft.server.level.ServerLevel level; + + public ServerWaypointManager(net.minecraft.server.level.ServerLevel level) { + this.level = level; + } + // DivineMC end - Parallel world ticking @Override public void trackWaypoint(WaypointTransmitter waypoint) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot track waypoints off-main"); // DivineMC - 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) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, "Cannot add player to waypoints off-main"); // DivineMC - 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 portalEntityTask = entity -> { + assert entity.portalProcess != null; + + if (entity.portalProcess.isParallelCancelledByPlugin()) { + entity.portalProcess = null; + return; + } + + TeleportTransition portalDestination = entity.portalProcess.getPortalDestination(serverLevel, entity); + if (portalDestination != null) { + ServerLevel level = portalDestination.newLevel(); + if (entity instanceof ServerPlayer // CraftBukkit - always call event for players + || (level != null && (level.dimension() == serverLevel.dimension() || entity.canTeleport(serverLevel, level)))) { // CraftBukkit + entity.teleport(portalDestination); + } } + + if (this.portalProcess != null) entity.portalProcess.confirmParallelAsHandled(); + }; + + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + this.portalProcess.setParallelAsScheduled(); + this.getBukkitEntity().taskScheduler.schedule(portalEntityTask, entity -> {}, 0); + } else { + portalEntityTask.accept(this); } + // DivineMC end - Parallel world ticking } else if (this.portalProcess.hasExpired()) { this.portalProcess = null; } @@ -4125,6 +4145,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name @Nullable private Entity teleportCrossDimension(ServerLevel oldLevel, ServerLevel newLevel, TeleportTransition teleportTransition) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(newLevel, "Cannot teleport entity to another world off-main, from world " + oldLevel.getWorld().getName() + " to world " + newLevel.getWorld().getName()); // DivineMC - Parallel world ticking List passengers = this.getPassengers(); List list = new ArrayList<>(passengers.size()); this.ejectPassengers(); diff --git a/net/minecraft/world/entity/PortalProcessor.java b/net/minecraft/world/entity/PortalProcessor.java index 91f6d43b3785ddad7db8eb529ba3293c45f3588d..7fd5f40ee928330769bbe0c5e8da17fa65b30db6 100644 --- a/net/minecraft/world/entity/PortalProcessor.java +++ b/net/minecraft/world/entity/PortalProcessor.java @@ -11,6 +11,7 @@ public class PortalProcessor { private BlockPos entryPosition; private int portalTime; private boolean insidePortalThisTick; + private org.bxteam.divinemc.async.world.TeleportState teleportState = org.bxteam.divinemc.async.world.TeleportState.INACTIVE; // DivineMC - Parallel world ticking public PortalProcessor(Portal portal, BlockPos entryPosition) { this.portal = portal; @@ -19,6 +20,7 @@ public class PortalProcessor { } public boolean processPortalTeleportation(ServerLevel level, Entity entity, boolean canChangeDimensions) { + if (this.isParallelTeleportScheduled()) return false; // DivineMC - Parallel world ticking if (!this.insidePortalThisTick) { this.decayTick(); return false; @@ -52,7 +54,7 @@ public class PortalProcessor { } public boolean hasExpired() { - return this.portalTime <= 0; + return !this.isParallelTeleportScheduled() && this.portalTime <= 0; // DivineMC - Parallel world ticking } public BlockPos getEntryPosition() { @@ -78,4 +80,30 @@ public class PortalProcessor { public boolean isSamePortal(Portal portal) { return this.portal == portal; } + + // DivineMC start - Parallel world ticking + public boolean isParallelTeleportPending() { + return this.teleportState == org.bxteam.divinemc.async.world.TeleportState.PENDING; + } + + public boolean isParallelTeleportScheduled() { + return this.teleportState != org.bxteam.divinemc.async.world.TeleportState.INACTIVE; + } + + public boolean isParallelCancelledByPlugin() { + return this.teleportState == org.bxteam.divinemc.async.world.TeleportState.CANCELLED; + } + + public void setParallelAsScheduled() { + this.teleportState = org.bxteam.divinemc.async.world.TeleportState.PENDING; + } + + public void confirmParallelAsHandled() { + this.teleportState = org.bxteam.divinemc.async.world.TeleportState.INACTIVE; + } + + public void setParallelAsCancelled() { + this.teleportState = org.bxteam.divinemc.async.world.TeleportState.CANCELLED; + } + // DivineMC end - Parallel world ticking } diff --git a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java index 6adf1b2250234ede6e6f498ed0990ab523f09b8e..a8ae790a7b9bfd0d78cac0577bb7a4ddac7f064b 100644 --- a/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java +++ b/net/minecraft/world/entity/ai/behavior/GoToPotentialJobSite.java @@ -43,15 +43,31 @@ public class GoToPotentialJobSite extends Behavior { Optional memory = entity.getBrain().getMemory(MemoryModuleType.POTENTIAL_JOB_SITE); memory.ifPresent(globalPos -> { BlockPos blockPos = globalPos.pos(); - ServerLevel level1 = level.getServer().getLevel(globalPos.dimension()); - if (level1 != null) { - PoiManager poiManager = level1.getPoiManager(); - if (poiManager.exists(blockPos, holder -> true)) { - poiManager.release(blockPos); - } + // DivineMC start - Parallel world ticking + ServerLevel entityLevel = level; + ServerLevel poiLevel = entityLevel.getServer().getLevel(globalPos.dimension()); + + if (poiLevel != null) { + Runnable poiOperationsTask = () -> { + PoiManager poiManager = poiLevel.getPoiManager(); + if (poiManager.exists(blockPos, holder -> true)) { + poiManager.release(blockPos); + } + }; - level.debugSynchronizers().updatePoi(blockPos); + Runnable debugPacketTask = () -> { + level.debugSynchronizers().updatePoi(blockPos); + }; + + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + poiLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(blockPos.getX() >> 4, blockPos.getZ() >> 4, poiOperationsTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + entityLevel.moonrise$getChunkTaskScheduler().scheduleChunkTask(entity.chunkPosition().x, entity.chunkPosition().z, debugPacketTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + } else { + poiOperationsTask.run(); + debugPacketTask.run(); + } } + // DivineMC end - Parallel world ticking }); entity.getBrain().eraseMemory(MemoryModuleType.POTENTIAL_JOB_SITE); } diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java index a356da48b20ab38648d75f9066b2ed4a013c13c2..415b021c8f0b68e2aacd23b568ca35d95bf2ce66 100644 --- a/net/minecraft/world/entity/npc/Villager.java +++ b/net/minecraft/world/entity/npc/Villager.java @@ -794,13 +794,24 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.brain.getMemory(moduleType).ifPresent(pos -> { ServerLevel level = server.getLevel(pos.dimension()); if (level != null) { - PoiManager poiManager = level.getPoiManager(); - Optional> type = poiManager.getType(pos.pos()); - BiPredicate> biPredicate = POI_MEMORIES.get(moduleType); - if (type.isPresent() && biPredicate.test(this, type.get())) { - poiManager.release(pos.pos()); - level.debugSynchronizers().updatePoi(pos.pos()); + // DivineMC start - Parallel world ticking + Runnable releasePoiTask = () -> { + PoiManager poiManager = level.getPoiManager(); + Optional> type = poiManager.getType(pos.pos()); + BiPredicate> biPredicate = POI_MEMORIES.get(moduleType); + if (type.isPresent() && biPredicate.test(this, type.get())) { + poiManager.release(pos.pos()); + level.debugSynchronizers().updatePoi(pos.pos()); + } + }; + + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + level.moonrise$getChunkTaskScheduler().scheduleChunkTask(0, 0, releasePoiTask, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING); + } + else { + releasePoiTask.run(); } + // DivineMC end - Parallel world ticking } }); } diff --git a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java index 9213d25928066ee6722f1a145ae37e99d6e62582..28f0b59500d67bdd92c97a7e138c94887caa043b 100644 --- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java +++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java @@ -104,43 +104,53 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { Vec3 vec3 = this.oldPosition(); if (owner instanceof ServerPlayer serverPlayer) { if (serverPlayer.connection.isAcceptingMessages()) { - // CraftBukkit start - // Store pre teleportation position as the teleport has been moved up. - final double preTeleportX = serverPlayer.getX(), preTeleportY = serverPlayer.getY(), preTeleportZ = serverPlayer.getZ(); - final float preTeleportYRot = serverPlayer.getYRot(), preTeleportXRot = serverPlayer.getXRot(); - ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); - if (serverPlayer1 == null) { - this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); - return; - } - // CraftBukkit end - if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.isSpawningMonsters()) { // Purpur - Configurable Ender Pearl RNG - Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); - if (endermite != null) { - endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API - endermite.snapTo(preTeleportX, preTeleportY, preTeleportZ, preTeleportYRot, preTeleportXRot); // Paper - spawn endermite at pre teleport position as teleport has been moved up - serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason + // DivineMC start - Parallel world ticking + java.util.function.Consumer teleportPlayerCrossDimensionTask = taskServerPlayer -> { + // CraftBukkit start + // Store pre teleportation position as the teleport has been moved up. + final double preTeleportX = serverPlayer.getX(), preTeleportY = serverPlayer.getY(), preTeleportZ = serverPlayer.getZ(); + final float preTeleportYRot = serverPlayer.getYRot(), preTeleportXRot = serverPlayer.getXRot(); + ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); + if (serverPlayer1 == null) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); + return; + } + // CraftBukkit end + if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.isSpawningMonsters()) { // Purpur - Configurable Ender Pearl RNG + Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); + if (endermite != null) { + endermite.setPlayerSpawned(true); // Purpur - Add back player spawned endermite API + endermite.snapTo(preTeleportX, preTeleportY, preTeleportZ, preTeleportYRot, preTeleportXRot); // Paper - spawn endermite at pre teleport position as teleport has been moved up + serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason + } } - } - if (this.isOnPortalCooldown()) { - owner.setPortalCooldown(); - } + if (this.isOnPortalCooldown()) { + owner.setPortalCooldown(); + } - // CraftBukkit start - moved up - // ServerPlayer serverPlayer1 = serverPlayer.teleport( - // new TeleportTransition( - // serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING - // ) - // ); - // CraftBukkit end - moved up - if (serverPlayer1 != null) { - serverPlayer1.resetFallDistance(); - serverPlayer1.resetCurrentImpulseContext(); - serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage - } + // CraftBukkit start - moved up + // ServerPlayer serverPlayer1 = serverPlayer.teleport( + // new TeleportTransition( + // serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING + // ) + // ); + // CraftBukkit end - moved up + if (serverPlayer1 != null) { + serverPlayer1.resetFallDistance(); + serverPlayer1.resetCurrentImpulseContext(); + serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - Configurable Ender Pearl damage + } - this.playSound(serverLevel, vec3); + this.playSound(serverLevel, vec3); + }; + + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + serverPlayer.getBukkitEntity().taskScheduler.schedule(teleportPlayerCrossDimensionTask, entity -> {}, 0); + } else { + teleportPlayerCrossDimensionTask.accept(serverPlayer); + } + // DivineMC end - Parallel world ticking } } else { Entity entity = owner.teleport( diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java index 276b5e6ac82e6e55e0a19c25a7c966501aeaba44..c3021c0a8c588acf5ae8c9231e75bda984863267 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -96,8 +96,14 @@ public abstract class AbstractContainerMenu { public void startOpen() {} // CraftBukkit end + public Throwable containerCreationStacktrace; // DivineMC - Parallel world ticking protected AbstractContainerMenu(@Nullable MenuType menuType, int containerId) { + // DivineMC start - Parallel world ticking (debugging) + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && org.bxteam.divinemc.config.DivineConfig.AsyncCategory.logContainerCreationStacktraces) { + this.containerCreationStacktrace = new Throwable(); + } + // DivineMC start - Parallel world ticking (debugging) this.menuType = menuType; this.containerId = containerId; } diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java index e4bcd4a3cbbc8942d52e5f3c7a4fb1572fdef91c..52a8a818a636e35b486b8aaf482cd68849a0bbcd 100644 --- a/net/minecraft/world/item/ItemStack.java +++ b/net/minecraft/world/item/ItemStack.java @@ -399,8 +399,10 @@ 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); - org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType; - net.minecraft.world.level.block.SaplingBlock.treeType = null; + // DivineMC start - Parallel world ticking + org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.getTreeTypeTL(); + net.minecraft.world.level.block.SaplingBlock.setTreeTypeTL(null); + // DivineMC end - Parallel world ticking 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 21a20aa602f51bbd6c8cdcb52f6b485971834595..7fe5cb2ea1c81b12baf302999a2794f20018707c 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl private int tileTickPosition; public final Map explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here + public io.papermc.paper.redstone.RedstoneWireTurbo turbo; // DivineMC - Parallel world ticking (moved to world) // Purpur start - Add adjustable breeding cooldown to config private com.google.common.cache.Cache playerBreedingCooldowns; @@ -921,6 +922,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl this.damageSources = new DamageSources(registryAccess); this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray + this.turbo = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? new io.papermc.paper.redstone.RedstoneWireTurbo((net.minecraft.world.level.block.RedStoneWireBlock) net.minecraft.world.level.block.Blocks.REDSTONE_WIRE) : null; // DivineMC - Parallel world ticking } // Paper start - Cancel hit for vanished players @@ -1089,6 +1091,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Override public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, pos, "Updating block asynchronously"); // DivineMC - 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 @@ -1472,11 +1475,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl this.blockEntityTickers.markAsRemoved(this.tileTickPosition); // DivineMC - optimize block entity removals - Fix MC-117075 } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { tickingBlockEntity.tick(); - // Paper start - rewrite chunk system - if ((++tickedEntities & 7) == 0) { - ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); + // DivineMC start - Parallel world ticking + ++tickedEntities; + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && (tickedEntities & 7) == 0) { + this.moonrise$midTickTasks(); } - // Paper end - rewrite chunk system + // DivineMC end - Parallel world ticking } } this.blockEntityTickers.removeMarkedEntries(); // DivineMC - optimize block entity removals - Fix MC-117075 @@ -1496,7 +1500,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Paper end - Prevent block entity and entity crashes } - this.moonrise$midTickTasks(); // Paper - rewrite chunk system + // DivineMC start - Parallel world ticking + if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + this.moonrise$midTickTasks(); // Paper - rewrite chunk system + } + // DivineMC end - Parallel world ticking } // Paper start - Option to prevent armor stands from doing entity lookups @@ -1638,6 +1646,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThreadOrAsyncThread((ServerLevel) this, "Cannot read world asynchronously"); // DivineMC - 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) { @@ -1654,6 +1663,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } public void setBlockEntity(BlockEntity blockEntity) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel) this, "Cannot modify world asynchronously"); // DivineMC - Parallel world ticking BlockPos blockPos = blockEntity.getBlockPos(); if (!this.isOutsideBuildHeight(blockPos)) { // CraftBukkit start @@ -1739,6 +1749,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Override public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread((ServerLevel)this, boundingBox, "Cannot getEntities asynchronously"); // DivineMC - Parallel world ticking (additional concurrency issues logs) List list = Lists.newArrayList(); // Paper start - rewrite chunk system @@ -2088,8 +2099,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl 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; + // DivineMC start - Parallel world ticking + int i; + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + i = this.random.nextInt() >> 2; + } else { + this.randValue = this.randValue * 3 + 1013904223; + i = this.randValue >> 2; + } + // DivineMC end - 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..ea310d53f728aaf5de3284b372ed393e4225a52f 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.setTreeTypeTL(org.bukkit.TreeType.WARPED_FUNGUS); // DivineMC - Parallel world ticking } else if (this == Blocks.CRIMSON_FUNGUS) { - SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS; + SaplingBlock.setTreeTypeTL(org.bukkit.TreeType.CRIMSON_FUNGUS); // DivineMC - Parallel world ticking } return value; }) diff --git a/net/minecraft/world/level/block/MushroomBlock.java b/net/minecraft/world/level/block/MushroomBlock.java index 9176cf8c47b19d76eb49a7c00fc6723836344d4b..05d173cb97b9c40d24b4c8485d509592a9afa9ad 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.setTreeTypeTL((this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM); // CraftBukkit // DivineMC - 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 a07d9237d227fe6d419d8a9bc44fc623b244ad3e..9f21d0a93a712538a15761c5834210856458bc78 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -66,6 +66,7 @@ public class RedStoneWireBlock extends Block { private final BlockState crossState; private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this); public boolean shouldSignal = true; + private final ThreadLocal shouldSignalTL = ThreadLocal.withInitial(() -> true); // DivineMC - Parallel world ticking @Override public MapCodec codec() { @@ -283,7 +284,13 @@ public class RedStoneWireBlock extends Block { if (orientation != null) { source = pos.relative(orientation.getFront().getOpposite()); } - turbo.updateSurroundingRedstone(worldIn, pos, state, source); + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + worldIn.turbo.updateSurroundingRedstone(worldIn, pos, state, source); + } else { + turbo.updateSurroundingRedstone(worldIn, pos, state, source); + } + // DivineMC end - Parallel world ticking return; } updatePowerStrength(worldIn, pos, state, orientation, blockAdded); @@ -311,7 +318,13 @@ 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); + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + level.turbo.updateNeighborShapes(level, pos, state); + } else { + turbo.updateNeighborShapes(level, pos, state); + } + // DivineMC end - Parallel world ticking } } } @@ -328,10 +341,19 @@ public class RedStoneWireBlock extends Block { } public int getBlockSignal(Level level, BlockPos pos) { - this.shouldSignal = false; - int bestNeighborSignal = level.getBestNeighborSignal(pos); - this.shouldSignal = true; - return bestNeighborSignal; + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + this.shouldSignalTL.set(false); + int bestNeighborSignal = level.getBestNeighborSignal(pos); + this.shouldSignalTL.set(true); + return bestNeighborSignal; + } else { + this.shouldSignal = false; + int bestNeighborSignal = level.getBestNeighborSignal(pos); + this.shouldSignal = true; + return bestNeighborSignal; + } + // DivineMC end - Parallel world ticking } private void checkCornerChangeAt(Level level, BlockPos pos) { @@ -422,12 +444,23 @@ public class RedStoneWireBlock extends Block { @Override protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction side) { - return !this.shouldSignal ? 0 : state.getSignal(level, pos, side); + // DivineMC start - Parallel world ticking + boolean signal = org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking ? this.shouldSignalTL.get() : this.shouldSignal; + return !signal ? 0 : state.getSignal(level, pos, side); + // DivineMC end - Parallel world ticking } @Override protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction side) { - if (this.shouldSignal && side != Direction.DOWN) { + // DivineMC start - Parallel world ticking + boolean signal; + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + signal = this.shouldSignalTL.get(); + } else { + signal = this.shouldSignal; + } + // DivineMC end - Parallel world ticking + if (signal && side != Direction.DOWN) { // DivineMC - Parallel world ticking int powerValue = state.getValue(POWER); if (powerValue == 0) { return 0; @@ -459,7 +492,13 @@ public class RedStoneWireBlock extends Block { @Override protected boolean isSignalSource(BlockState state) { - return this.shouldSignal; + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) { + return this.shouldSignalTL.get(); + } else { + return this.shouldSignal; + } + // DivineMC end - Parallel world ticking } public static int getColorForPower(int power) { diff --git a/net/minecraft/world/level/block/SaplingBlock.java b/net/minecraft/world/level/block/SaplingBlock.java index 23e9e5e7ef76fe3d6e1bbc41faf69ee65ca77d80..9d52904766fd48902b85bdc34ffafcd874c77ad0 100644 --- a/net/minecraft/world/level/block/SaplingBlock.java +++ b/net/minecraft/world/level/block/SaplingBlock.java @@ -26,6 +26,26 @@ public class SaplingBlock extends VegetationBlock implements BonemealableBlock { 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 + // DivineMC start - Parallel world ticking + public static final ThreadLocal treeTypeTL = new ThreadLocal<>(); + + public static org.bukkit.TreeType getTreeTypeTL() { + org.bukkit.TreeType treeTypeRTCopy; + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && (treeTypeRTCopy = treeTypeTL.get()) != null) return treeTypeRTCopy; + + synchronized (SaplingBlock.class) { + return treeType; + } + } + + public static void setTreeTypeTL(org.bukkit.TreeType value) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) treeTypeTL.set(value); + + synchronized (SaplingBlock.class) { + treeType = value; + } + } + // DivineMC end - Parallel world ticking @Override public MapCodec codec() { @@ -62,14 +82,16 @@ 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; + // DivineMC start - Parallel world ticking + org.bukkit.TreeType treeTypeLocal = SaplingBlock.getTreeTypeTL(); + SaplingBlock.setTreeTypeTL(null); + // DivineMC end - Parallel world ticking org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level); java.util.List blocks = new java.util.ArrayList<>(level.capturedBlockStates.values()); level.capturedBlockStates.clear(); org.bukkit.event.world.StructureGrowEvent event = null; - if (treeType != null) { - event = new org.bukkit.event.world.StructureGrowEvent(location, treeType, false, null, blocks); + if (treeTypeLocal != null) { // DivineMC - Parallel world ticking + event = new org.bukkit.event.world.StructureGrowEvent(location, treeTypeLocal, false, null, blocks); // DivineMC - Parallel world ticking org.bukkit.Bukkit.getPluginManager().callEvent(event); } if (event == null || !event.isCancelled()) { diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java index 1b2f8c4e1e362dc63fde2c7139039f0ce7eb762f..7c2acea8af6a3110d782b9b3afeac0915ac127da 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) { + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && player instanceof net.minecraft.server.level.ServerPlayer serverPlayer && blockEntity != null && blockEntity.getLevel() != serverPlayer.level()) { + net.minecraft.server.MinecraftServer.LOGGER.warn("Player {} ({}) attempted to open a BlockEntity @ {} {}, {}, {} while they were in a different world {} than the block themselves!", serverPlayer.getScoreboardName(), serverPlayer.getStringUUID(), blockEntity.getLevel().getWorld().getName(), blockEntity.getBlockPos().getX(), blockEntity.getBlockPos().getY(), blockEntity.getBlockPos().getZ(), serverPlayer.level().getWorld().getName()); + return false; + } + // DivineMC end - Parallel world ticking 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 ab7be4298be22af32c4726f178b93896b96f599d..d78dc310002ebb4149b60a35bf514f1fd49f95c9 100644 --- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java @@ -44,9 +44,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 3efebec19a1b4fefd6071247c614693e44bc3eb4..e71eb14879afc59298c87714062f7c4ee5ad0a8a 100644 --- a/net/minecraft/world/level/block/grower/TreeGrower.java +++ b/net/minecraft/world/level/block/grower/TreeGrower.java @@ -203,55 +203,59 @@ public final class TreeGrower { // CraftBukkit start private void setTreeType(Holder> feature) { + // DivineMC start - Parallel world ticking + org.bukkit.TreeType treeType; 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; } 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; } 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; } 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; } 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; } else if (feature.is(TreeFeatures.PINE)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD; + treeType = org.bukkit.TreeType.TALL_REDWOOD; } else if (feature.is(TreeFeatures.SPRUCE)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD; + treeType = org.bukkit.TreeType.REDWOOD; } else if (feature.is(TreeFeatures.ACACIA)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA; + treeType = org.bukkit.TreeType.ACACIA; } 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; } 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; } else if (feature.is(TreeFeatures.SWAMP_OAK)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP; + treeType = org.bukkit.TreeType.SWAMP; } 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; } 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; } 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; } 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; } 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; } else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE; + treeType = org.bukkit.TreeType.JUNGLE; } else if (feature.is(TreeFeatures.AZALEA_TREE)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA; + treeType = org.bukkit.TreeType.AZALEA; } else if (feature.is(TreeFeatures.MANGROVE)) { - net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE; + treeType = org.bukkit.TreeType.MANGROVE; } 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; } 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; } 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; } 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; } else { throw new IllegalArgumentException("Unknown tree generator " + feature); } + net.minecraft.world.level.block.SaplingBlock.setTreeTypeTL(treeType); + // DivineMC end - Parallel world ticking } // CraftBukkit end } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java index 2d4807134e071254ce882e0bc4988242894c5d2f..40500e24156049e09a9970b120556f8aa49ffcaf 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -372,6 +372,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot @Nullable @Override public BlockState setBlockState(BlockPos pos, BlockState state, int flags) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, pos, "Updating block asynchronously"); // DivineMC - Parallel world ticking 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 c89701d7bdc9b889038d3c52f2232fb17624b113..42cb7885a0073d872676e32cf6b1253a7166a561 100644 --- a/net/minecraft/world/level/entity/EntityTickList.java +++ b/net/minecraft/world/level/entity/EntityTickList.java @@ -11,25 +11,67 @@ import net.minecraft.world.entity.Entity; public class EntityTickList { public final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet entities = new ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet<>(); // Paper - rewrite chunk system + // DivineMC start - Parallel world ticking + @Nullable + private final net.minecraft.server.level.ServerLevel serverLevel; + + public EntityTickList() { + this(null); + } + + public EntityTickList(@Nullable net.minecraft.server.level.ServerLevel serverLevel) { + this.serverLevel = serverLevel; + } + // DivineMC end - Parallel world ticking + private void ensureActiveIsNotIterated() { // Paper - rewrite chunk system } public void add(Entity entity) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist addition"); // DivineMC - Parallel world ticking this.ensureActiveIsNotIterated(); - this.entities.add(entity); // Paper - rewrite chunk system + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.RCTorPWT) { + synchronized (this.entities) { + this.entities.add(entity); + } + } else { + this.entities.add(entity); // Paper - rewrite chunk system + } + // DivineMC end - Parallel world ticking } public void remove(Entity entity) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(entity, "Asynchronous entity ticklist removal"); // DivineMC - Parallel world ticking this.ensureActiveIsNotIterated(); - this.entities.remove(entity); // Paper - rewrite chunk system + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.RCTorPWT) { + synchronized (this.entities) { + if (this.entities.contains(entity)) { + this.entities.remove(entity); + } + } + } else { + this.entities.remove(entity); // Paper - rewrite chunk system + } + // DivineMC end - Parallel world ticking } public boolean contains(Entity entity) { - return this.entities.contains(entity); // Paper - rewrite chunk system + // DivineMC start - Parallel world ticking + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.RCTorPWT) { + synchronized (this.entities) { + return this.entities.contains(entity); + } + } else { + return this.entities.contains(entity); // Paper - rewrite chunk system + } + // DivineMC end - Parallel world ticking } public void forEach(Consumer entity) { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(serverLevel, "Asynchronous entity ticklist iteration"); // DivineMC - Parallel world ticking // 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..53e6a478eafec512be42422898e7fb334015946b 100644 --- a/net/minecraft/world/level/saveddata/maps/MapIndex.java +++ b/net/minecraft/world/level/saveddata/maps/MapIndex.java @@ -23,8 +23,12 @@ public class MapIndex extends SavedData { } public MapId getNextMapId() { - MapId mapId = new MapId(++this.lastMapId); - this.setDirty(); - return mapId; + // DivineMC start - Parallel world ticking + synchronized (TYPE) { + MapId mapId = new MapId(++this.lastMapId); + this.setDirty(); + return mapId; + } + // DivineMC end - Parallel world ticking } }