From e97b1eee63b802568447d344bac5a66bf90c6c71 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 21 Feb 2024 16:23:51 -0800 Subject: [PATCH] Move Connection tick to after world tick The world tick will synchronise block data to clients. If the connection tick is ran before, then the server will send the block ack before the block updates - which causes the client to think that any blocks it broke/placed were rejected. Fixes https://github.com/PaperMC/Folia/issues/194 --- patches/server/0003-Threaded-Regions.patch | 90 +++++++++---------- ...0007-Disable-mid-tick-task-execution.patch | 2 +- patches/server/0018-Region-profiler.patch | 30 ++++--- 3 files changed, 59 insertions(+), 63 deletions(-) diff --git a/patches/server/0003-Threaded-Regions.patch b/patches/server/0003-Threaded-Regions.patch index d56e12fab0..3232086225 100644 --- a/patches/server/0003-Threaded-Regions.patch +++ b/patches/server/0003-Threaded-Regions.patch @@ -1638,7 +1638,7 @@ index 15ee41452992714108efe53b708b5a4e1da7c1ff..5bef4f50082e56b89239cfd62dd74299 } diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java -index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07bd11f0d56 100644 +index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..67bf841878eb8e3703782caeb16db4803d13f0d9 100644 --- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java +++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java @@ -53,6 +53,14 @@ import java.util.concurrent.atomic.AtomicReference; @@ -1656,7 +1656,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b public final class ChunkHolderManager { private static final Logger LOGGER = LogUtils.getClassLogger(); -@@ -112,27 +120,92 @@ public final class ChunkHolderManager { +@@ -112,27 +120,83 @@ public final class ChunkHolderManager { private final ChunkTaskScheduler taskScheduler; private long currentTick; @@ -1666,15 +1666,6 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b - return 0; + // Folia start - region threading + public static final class HolderManagerRegionData { -+ /* -+ * This region data is a bit of a mess, because it is part global state and part region state. -+ * Typically for region state we do not need to worry about threading concerns because it is only -+ * accessed by the current region when ticking. But since this contains state ( -+ * tickets, and removeTickToChunkExpireTicketCount) that can be written to by any thread holding the -+ * ticket lock, the merge logic is complicated as merging only holds the region lock. So, Folia has modified -+ * the add and remove ticket functions to acquire the region lock if the current region does not own the target -+ * position. -+ */ + private final ArrayDeque pendingFullLoadUpdate = new ArrayDeque<>(); + private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { + if (c1 == c2) { @@ -1716,9 +1707,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b + for (final NewChunkHolder fullLoadUpdate : this.pendingFullLoadUpdate) { + final int regionCoordinateX = fullLoadUpdate.chunkX >> chunkToRegionShift; + final int regionCoordinateZ = fullLoadUpdate.chunkZ >> chunkToRegionShift; - -- if (saveTickCompare != 0) { -- return saveTickCompare; ++ + final HolderManagerRegionData data = regionToData.get(CoordinateUtils.getChunkKey(regionCoordinateX, regionCoordinateZ)); + if (data != null) { + data.pendingFullLoadUpdate.add(fullLoadUpdate); @@ -1728,7 +1717,9 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b + for (final NewChunkHolder autoSave : this.autoSaveQueue) { + final int regionCoordinateX = autoSave.chunkX >> chunkToRegionShift; + final int regionCoordinateZ = autoSave.chunkZ >> chunkToRegionShift; -+ + +- if (saveTickCompare != 0) { +- return saveTickCompare; + final HolderManagerRegionData data = regionToData.get(CoordinateUtils.getChunkKey(regionCoordinateX, regionCoordinateZ)); + if (data != null) { + data.autoSaveQueue.add(autoSave); @@ -1762,7 +1753,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) { this.world = world; -@@ -167,8 +240,13 @@ public final class ChunkHolderManager { +@@ -167,8 +231,13 @@ public final class ChunkHolderManager { } public void close(final boolean save, final boolean halt) { @@ -1777,7 +1768,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b LOGGER.info("Waiting 60s for chunk system to halt for world '" + this.world.getWorld().getName() + "'"); if (!this.taskScheduler.halt(true, TimeUnit.SECONDS.toNanos(60L))) { LOGGER.warn("Failed to halt world generation/loading tasks for world '" + this.world.getWorld().getName() + "'"); -@@ -178,9 +256,10 @@ public final class ChunkHolderManager { +@@ -178,9 +247,10 @@ public final class ChunkHolderManager { } if (save) { @@ -1789,7 +1780,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b if (this.world.chunkDataControllerNew.hasTasks() || this.world.entityDataControllerNew.hasTasks() || this.world.poiDataControllerNew.hasTasks()) { RegionFileIOThread.flush(); } -@@ -201,27 +280,34 @@ public final class ChunkHolderManager { +@@ -201,27 +271,34 @@ public final class ChunkHolderManager { } catch (final IOException ex) { LOGGER.error("Failed to close poi regionfile cache for world '" + this.world.getWorld().getName() + "'", ex); } @@ -1831,7 +1822,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b holder.lastAutoSave = currentTick; if (holder.save(false, false) != null) { -@@ -235,15 +321,38 @@ public final class ChunkHolderManager { +@@ -235,15 +312,38 @@ public final class ChunkHolderManager { for (final NewChunkHolder holder : reschedule) { if (holder.getChunkStatus().isOrAfter(FullChunkStatus.FULL)) { @@ -1873,7 +1864,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b LOGGER.info("Saving all chunkholders for world '" + this.world.getWorld().getName() + "'"); } -@@ -251,7 +360,7 @@ public final class ChunkHolderManager { +@@ -251,7 +351,7 @@ public final class ChunkHolderManager { int saved = 0; @@ -1882,7 +1873,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b long lastLog = start; boolean needsFlush = false; final int flushInterval = 50; -@@ -262,6 +371,12 @@ public final class ChunkHolderManager { +@@ -262,6 +362,12 @@ public final class ChunkHolderManager { for (int i = 0, len = holders.size(); i < len; ++i) { final NewChunkHolder holder = holders.get(i); @@ -1895,7 +1886,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b try { final NewChunkHolder.SaveStat saveStat = holder.save(shutdown, false); if (saveStat != null) { -@@ -294,7 +409,7 @@ public final class ChunkHolderManager { +@@ -294,7 +400,7 @@ public final class ChunkHolderManager { } } } @@ -1904,7 +1895,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b RegionFileIOThread.flush(); if (this.world.paperConfig().chunks.flushRegionsOnSave) { try { -@@ -707,6 +822,13 @@ public final class ChunkHolderManager { +@@ -707,6 +813,13 @@ public final class ChunkHolderManager { } public void tick() { @@ -1918,7 +1909,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b final int sectionShift = this.world.getRegionChunkShift(); final Predicate> expireNow = (final Ticket ticket) -> { -@@ -716,12 +838,12 @@ public final class ChunkHolderManager { +@@ -716,12 +829,12 @@ public final class ChunkHolderManager { return --ticket.removeDelay <= 0L; }; @@ -1936,7 +1927,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b continue; } -@@ -1024,19 +1146,51 @@ public final class ChunkHolderManager { +@@ -1024,19 +1137,51 @@ public final class ChunkHolderManager { if (changedFullStatus.isEmpty()) { return; } @@ -2000,7 +1991,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b } } } -@@ -1044,8 +1198,9 @@ public final class ChunkHolderManager { +@@ -1044,8 +1189,9 @@ public final class ChunkHolderManager { private void removeChunkHolder(final NewChunkHolder holder) { holder.killed = true; holder.vanillaChunkHolder.onChunkRemove(); @@ -2011,7 +2002,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b synchronized (this.chunkHolders) { this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ)); } -@@ -1059,7 +1214,7 @@ public final class ChunkHolderManager { +@@ -1059,7 +1205,7 @@ public final class ChunkHolderManager { throw new IllegalStateException("Cannot unload chunks recursively"); } final int sectionShift = this.unloadQueue.coordinateShift; // sectionShift <= lock shift @@ -2020,7 +2011,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b int unloadCountTentative = 0; for (final ChunkQueue.SectionToUnload sectionRef : unloadSectionsForRegion) { final ChunkQueue.UnloadSection section -@@ -1372,7 +1527,13 @@ public final class ChunkHolderManager { +@@ -1372,7 +1518,13 @@ public final class ChunkHolderManager { // only call on tick thread protected final boolean processPendingFullUpdate() { @@ -2035,7 +2026,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b boolean ret = false; -@@ -1383,9 +1544,7 @@ public final class ChunkHolderManager { +@@ -1383,9 +1535,7 @@ public final class ChunkHolderManager { ret |= holder.handleFullStatusChange(changedFullStatus); if (!changedFullStatus.isEmpty()) { @@ -2046,7 +2037,7 @@ index 6bc7c6f16a1649fc9e24e7cf90fca401e5bd4875..30259130f23dc07288a7cbb33456b07b changedFullStatus.clear(); } } -@@ -1399,7 +1558,7 @@ public final class ChunkHolderManager { +@@ -1399,7 +1549,7 @@ public final class ChunkHolderManager { private JsonObject getDebugJsonNoLock() { final JsonObject ret = new JsonObject(); @@ -10075,7 +10066,7 @@ index 3e2d5dcd62775b6ed7c0ce0ba51a71b635b1d644..98fb69a9adeb6eaab199aec127692acb // CraftBukkit start - SPIGOT-5477, MC-142590 } else if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerCommonPacketListenerImpl && ((ServerCommonPacketListenerImpl) listener).processedDisconnect)) { diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 680919045310be7e50e2d503a23d265f2e9b2bc6..0330f180ef8fed3091b69758cea64d742a4dc0ac 100644 +index 680919045310be7e50e2d503a23d265f2e9b2bc6..11eb550cba3a8eee1deb2f00e5514863e255ea07 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -203,7 +203,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { return worldserver + " " + worldserver.dimension().location(); -@@ -1711,7 +1792,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() % autosavePeriod == 0; // Folia - region threading @@ -1428,7 +1424,7 @@ index b912a5d6737cf8bd74617225ca0837e6e97b7206..b73699a08a368e6305759438c00066b0 try { this.isSaving = true; if (playerSaveInterval > 0) { -@@ -1663,6 +1676,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { // Folia - region threading entityplayer.connection.suspendFlushing(); -@@ -1793,12 +1808,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop