Set block during chunk unload: "Chunk not there when requested: Unloaded chunk"

    • Type: Bug
    • Resolution: Fixed
    • Priority: Minor
    • None
    • Affects Version/s: None
    • This server is running CraftBukkit version dev-Spigot-146439e-3f8e416 (MC: 1.21) (Implementing API version 1.21-R0.1-SNAPSHOT)
    • Yes

      A plugin tries to update a block during the ChunkUnloadEvent, before the chunk gets unloaded.

      In previous versions this worked fine (tested with Spigot for MC 1.20.6): The event was called before the server actually considers the chunk as unloaded, allowing plugins to still process the chunk during the event before the chunk is actually moved to the unloaded state. In 1.21 (even with the recent bug fix related to chunk unloads included: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/f5a63f7345eaf167d2664dc04f4e2cf7458c26fc), the following error is observed:

      java.lang.IllegalStateException: Chunk not there when requested: Unloaded chunk
              at net.minecraft.server.level.ChunkProviderServer.a(ChunkProviderServer.java:156) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.world.level.World.a(World.java:298) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.world.level.IWorldReader.a(SourceFile:156) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.world.level.World.d(World.java:292) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.world.level.World.a_(World.java:547) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.getNMS(CraftBlock.java:79) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setTypeAndData(CraftBlock.java:192) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setBlockData(CraftBlock.java:188) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at org.bukkit.craftbukkit.v1_21_R1.block.CraftBlock.setType(CraftBlock.java:177) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at com.nisovin.shopkeepers.shopobjects.block.base.BaseBlockShopObject.despawn(BaseBlockShopObject.java:186) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.doDespawnShopkeeper(ShopkeeperSpawner.java:442) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.despawnShopkeeper(ShopkeeperSpawner.java:330) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.spawning.ShopkeeperSpawner.despawnChunkShopkeepers(ShopkeeperSpawner.java:651) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.activation.ShopkeeperChunkActivator.deactivateChunk(ShopkeeperChunkActivator.java:576) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.activation.ShopkeeperChunkActivator.onChunkUnload(ShopkeeperChunkActivator.java:517) ~[?:?]
              at com.nisovin.shopkeepers.shopkeeper.activation.ChunkActivationListener.onChunkUnload(ChunkActivationListener.java:58) ~[?:?]
              at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
              at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
              at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:306) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
              at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
              at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:601) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
              at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:588) ~[spigot-api-1.21-R0.1-SNAPSHOT.jar:?]
              at net.minecraft.world.level.chunk.Chunk.unloadCallback(Chunk.java:590) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.PlayerChunk.lambda$updateFutures$7(PlayerChunk.java:324) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.PlayerChunkMap$CallbackExecutor.run(PlayerChunkMap.java:167) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.PlayerChunk.a(PlayerChunk.java:334) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.ChunkMapDistance.lambda$runAllUpdates$1(ChunkMapDistance.java:128) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[?:?]
              at net.minecraft.server.level.ChunkMapDistance.a(ChunkMapDistance.java:127) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.ChunkProviderServer.s(ChunkProviderServer.java:290) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.ChunkProviderServer$b.B(ChunkProviderServer.java:614) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.level.ChunkProviderServer.d(ChunkProviderServer.java:286) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.bv(MinecraftServer.java:1258) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.B(MinecraftServer.java:1242) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.util.thread.IAsyncTaskHandler.b(SourceFile:145) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1207) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.v_(MinecraftServer.java:1214) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.y(MinecraftServer.java:1078) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:318) ~[spigot-1.21-R0.1-SNAPSHOT.jar:dev-Spigot-146439e-3f8e416]
              at java.base/java.lang.Thread.run(Thread.java:1583) [?:?]
      

      Edit: Maybe caused by: Seems like there is a new check in GenerationChunkHolder.scheduleChunkGenerationTask -> isStatusDisallowed which is already reduced in ChunkMapDistance.runAllUpdates based on the new ticket level (updateHighestAllowedStatus), before the unload callback is handled (updateFutures).

          [SPIGOT-7780] Set block during chunk unload: "Chunk not there when requested: Unloaded chunk"

          Irmo van den Berge added a comment - - edited

          I made a very simple plugin to test this issue. https://ci.mg-dev.eu/view/all/job/ChunkUnloadTest/2/

          Irmo van den Berge added a comment - - edited I made a very simple plugin to test this issue. https://ci.mg-dev.eu/view/all/job/ChunkUnloadTest/2/

          Irmo van den Berge added a comment - - edited

          blablubbabc I'll have a look tomorrow. I know the chunk system quite well, but to be honest, I haven't delved that deeply into it since like version 1.14. But I can at least test your PR's changes to see if they solve the issue (and don't result in something like the unload event firing twice)

          Irmo van den Berge added a comment - - edited blablubbabc I'll have a look tomorrow. I know the chunk system quite well, but to be honest, I haven't delved that deeply into it since like version 1.14. But I can at least test your PR 's changes to see if they solve the issue (and don't result in something like the unload event firing twice)

          blablubbabc added a comment -

          bergerkiller I have opened a PR for this with a potential solution that appears to work in my basic testing. Since you seem to have some knowledge about the inner workings of Minecraft chunk loading, please consider taking a look and let me know if you can see any issues with it.

          blablubbabc added a comment - bergerkiller I have opened a PR for this with a potential solution that appears to work in my basic testing. Since you seem to have some knowledge about the inner workings of Minecraft chunk loading, please consider taking a look and let me know if you can see any issues with it.

          Tau added a comment -

          Indeed, this is a major issue.

          For plugins that rely on chunkunloadevent to save changes, this could cause dupes.
          Imagine if this happened in vanilla, and your chests didn't save when the chunk unloads. It would immediately be considered a major issue.

          Tau added a comment - Indeed, this is a major issue. For plugins that rely on chunkunloadevent to save changes, this could cause dupes. Imagine if this happened in vanilla, and your chests didn't save when the chunk unloads. It would immediately be considered a major issue.

          Irmo van den Berge added a comment - - edited

          My current workaround is to keep track of a cache of Chunk handles mapped to Bukkit chunk myself, and keep them around for the duration of chunk unload event. Then I replaced all bukkit's apis like getBlockStates() with my own that uses that NMS handle from cache. A tick delayed these handles are pruned from the cache automatically.

          The handle still contains the tile entities, so this works.

          Irmo van den Berge added a comment - - edited My current workaround is to keep track of a cache of Chunk handles mapped to Bukkit chunk myself, and keep them around for the duration of chunk unload event. Then I replaced all bukkit's apis like getBlockStates() with my own that uses that NMS handle from cache. A tick delayed these handles are pruned from the cache automatically. The handle still contains the tile entities, so this works.

          It's been a very long time now and this is still not fixed. I'm noticing priority is minor, is this why? This breaks my code for checking what signs exist inside a chunk at time of unloading, and is very important. This isn't just limited to setting blocks on unload.

          I'm kind of puzzled by this issue existing for months.

          Irmo van den Berge added a comment - It's been a very long time now and this is still not fixed. I'm noticing priority is minor, is this why? This breaks my code for checking what signs exist inside a chunk at time of unloading, and is very important. This isn't just limited to setting blocks on unload. I'm kind of puzzled by this issue existing for months.

            Assignee:
            Unassigned
            Reporter:
            blablubbabc
            Votes:
            5 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: