From b1e0ad4ef7f39a5d376b374180f69930927168c3 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 15 Jul 2023 16:41:04 +0100 Subject: [PATCH] feat: re-submit chunk load request after 10s after checking world is loaded (#2339) - #2332 makes it seem like paper forgets to load a chunk sometimes - resubmit chunk load request after a second to attempt to counter this --- .../PaperweightPlatformAdapter.java | 23 ++++++++++++++- .../v1_18_R2/PaperweightPlatformAdapter.java | 28 +++++++++++++++---- .../v1_19_R3/PaperweightPlatformAdapter.java | 26 ++++++++++++++--- .../v1_20_R1/PaperweightPlatformAdapter.java | 23 ++++++++++++++- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java index 69ab5fe9b..688a84a14 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java @@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -48,6 +49,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.gameevent.GameEventDispatcher; import net.minecraft.world.level.gameevent.GameEventListener; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; import sun.misc.Unsafe; @@ -61,6 +64,8 @@ import java.util.Locale; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.stream.Stream; @@ -91,6 +96,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static { try { fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l")); @@ -225,7 +232,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } return chunk.getHandle(); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java index 8e982a84e..dbf7f88f3 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java @@ -13,17 +13,16 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; @@ -41,7 +40,6 @@ import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.HashMapPalette; @@ -51,8 +49,8 @@ import net.minecraft.world.level.chunk.LinearPalette; import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; -import net.minecraft.world.level.gameevent.GameEventDispatcher; -import net.minecraft.world.level.gameevent.GameEventListener; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_18_R2.CraftChunk; import sun.misc.Unsafe; @@ -75,6 +73,8 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; public final class PaperweightPlatformAdapter extends NMSAdapter { @@ -106,6 +106,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static { try { fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); @@ -253,7 +255,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } return chunk.getHandle(); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java index 5af8f2806..d351bbacd 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java @@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -43,7 +44,6 @@ import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.HashMapPalette; @@ -54,6 +54,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; import sun.misc.Unsafe; @@ -61,7 +63,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -77,9 +78,10 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; -import static java.lang.invoke.MethodType.methodType; import static net.minecraft.core.registries.Registries.BIOME; public final class PaperweightPlatformAdapter extends NMSAdapter { @@ -111,6 +113,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static final boolean POST_CHUNK_REWRITE; private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES; @@ -287,7 +291,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } addTicket(serverLevel, chunkX, chunkZ); return (LevelChunk) chunk.getHandle(ChunkStatus.FULL); } catch (Throwable e) { diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java index f66e7dd6a..8cb8a8662 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java @@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -54,6 +55,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; import sun.misc.Unsafe; @@ -77,6 +80,8 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import static java.lang.invoke.MethodType.methodType; @@ -117,6 +122,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static final boolean POST_CHUNK_REWRITE; private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES; @@ -307,7 +314,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } addTicket(serverLevel, chunkX, chunkZ); return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); } catch (Throwable e) {