[SPIGOT-7780] Set block during chunk unload: "Chunk not there when requested: Unloaded chunk" Created: 18/Jun/24 Updated: 25/Dec/24 Resolved: 26/Sep/24 |
|
Status: | Resolved |
Project: | Spigot |
Component/s: | None |
Affects Version/s: | None |
Fix Version/s: | None |
Type: | Bug | Priority: | Minor |
Reporter: | blablubbabc | Assignee: | Unassigned |
Resolution: | Fixed | Votes: | 5 |
Labels: | ChunkUnloadEvent |
Issue Links: |
|
||||||||||||||||
Version: | This server is running CraftBukkit version dev-Spigot-146439e-3f8e416 (MC: 1.21) (Implementing API version 1.21-R0.1-SNAPSHOT) | ||||||||||||||||
Guidelines Read: | Yes |
Description |
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). |
Comments |
Comment by Irmo van den Berge [ 25/Sep/24 ] |
I made a very simple plugin to test this issue. https://ci.mg-dev.eu/view/all/job/ChunkUnloadTest/2/ |
Comment by Irmo van den Berge [ 25/Sep/24 ] |
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) |
Comment by blablubbabc [ 24/Sep/24 ] |
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. |
Comment by Tau [ 18/Sep/24 ] |
Indeed, this is a major issue. For plugins that rely on chunkunloadevent to save changes, this could cause dupes. |
Comment by Irmo van den Berge [ 18/Sep/24 ] |
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. |
Comment by Irmo van den Berge [ 18/Sep/24 ] |
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. |