https://linen.dev logo
Chunk Regeneration
# open-problems
c
If a chunk with tile ticks in it is saved, and that chunk is loaded on one thread, while another thread is executing tile ticks, the chunk can sometimes be regenerated.
@Myren did i get this right?
LETS GO IT WORKS!!!!!!!!!!!!!!!
you currently have to turn on itt with commands because i was too lazy to redo the itt switch when the liquid pocket changed location
[18:39:56] [Server thread/INFO]: Stopping server [18:39:56] [Server thread/INFO]: Saving players [18:39:56] [Server thread/INFO]: Saving worlds [18:39:56] [Server thread/INFO]: Saving chunks for level 'world'/overworld commonly it gets stuck here and stays until watchdog kills it
[18:39:56] [Downloader 1/ERROR]: Couldn't load chunk java.lang.NullPointerException: null at java.util.TreeMap.rotateLeft(TreeMap.java:2224) ~[?:1.8.0_202] at java.util.TreeMap.fixAfterInsertion(TreeMap.java:2291) ~[?:1.8.0_202] at java.util.TreeMap.put(TreeMap.java:582) ~[?:1.8.0_202] at java.util.TreeSet.add(TreeSet.java:255) ~[?:1.8.0_202] at oo.b(WorldServer.java:828) ~[ThreadstoneCarpet.jar:?] at aye.a(AnvilChunkLoader.java:566) ~[ThreadstoneCarpet.jar:?] at aye.a(AnvilChunkLoader.java:164) ~[ThreadstoneCarpet.jar:?] at aye.a(AnvilChunkLoader.java:129) ~[ThreadstoneCarpet.jar:?] at on.f(ChunkProviderServer.java:199) [ThreadstoneCarpet.jar:?] at on.b(ChunkProviderServer.java:129) [ThreadstoneCarpet.jar:?] at on.c(ChunkProviderServer.java:161) [ThreadstoneCarpet.jar:?] at amu.a(World.java:353) [ThreadstoneCarpet.jar:?] at amu.f(World.java:345) [ThreadstoneCarpet.jar:?] at amu.o(World.java:981) [ThreadstoneCarpet.jar:?] at aot$1.run(BlockBeacon.java:152) [ThreadstoneCarpet.jar:?] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_202] at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:111) [ThreadstoneCarpet.jar:?] at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:58) [ThreadstoneCarpet.jar:?] at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:75) [ThreadstoneCarpet.jar:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_202] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_202] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_202] [18:39:56] [Server thread/ERROR]: Encountered an unexpected exception f: Exception ticking world at net.minecraft.server.MinecraftServer.D(MinecraftServer.java:886) ~[ThreadstoneCarpet.jar:?] at nz.D(DedicatedServer.java:410) ~[ThreadstoneCarpet.jar:?] at net.minecraft.server.MinecraftServer.C(MinecraftServer.java:754) ~[ThreadstoneCarpet.jar:?] at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:607) [ThreadstoneCarpet.jar:?] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_202] Caused by: java.lang.IllegalStateException: TickNextTick list out of synch at oo.a(WorldServer.java:939) ~[ThreadstoneCarpet.jar:?] at oo.d(WorldServer.java:300) ~[ThreadstoneCarpet.jar:?] at net.minecraft.server.MinecraftServer.D(MinecraftServer.java:875) ~[ThreadstoneCarpet.jar:?] ... 4 more
t
c
these exceptions are what i get
i wonder if there is a way to make this fly
because it would be quite useful if so
m
The other thread doesn't need to execute tile ticks. The other thread can also schedule tile ticks for this.
You could now make a void's synchronized setup for getting an async line that doesn't require you to savestate any unpopulated chunks.
You get the population by just regenerating the glass chunk.
So instead of having the fire in the chunk you want to regenerate, you could also have fire right outside it all along the chunk border
It saves all tile ticks in this bounding box
Copy code
public List<NextTickListEntry> getPendingBlockUpdates(Chunk chunkIn, boolean p_72920_2_)
    {
        ChunkPos chunkpos = chunkIn.getChunkCoordIntPair();
        int i = (chunkpos.chunkXPos << 4) - 2;
        int j = i + 16 + 2;
        int k = (chunkpos.chunkZPos << 4) - 2;
        int l = k + 16 + 2;
        return this.getPendingBlockUpdates(new StructureBoundingBox(i, 0, k, j, 256, l), p_72920_2_);
    }
In that world file I need to add another main thread lagspike, because otherwise the main thread just loads the glass chunk in the player phase.
I managed to get this crash. It didn't regen the chunk for me, but with this crash I am convinced that chunk regen would happen if I tried more. https://cdn.discordapp.com/attachments/1527072941689278604/1527234751503532154/crash-2026-07-16_10.39.35-server.txt?ex=6a59ebb3&is=6a589a33&hm=e6b6a828bff4840ebd8626d979073f9d60a3146fc1c486e8a5f14f76f3aae227&
I think the biggest open problem right now would be to do a chunk regen without shutting down the server.
And this could actually have a lot of applications, if it allows us to get async lines without having to savestate any unpopulated chunks.
c
i never needed to do that, but good to know
thats the wrong crash that i sometimes get
the correct one is TileNextTickList Out Of Sync
"The Ring Of Fire"
this was most of the theory behind the regen-synchronized method
yo that might make forge falling block much easier too
most synchronized methods actually do that, idk why i didnt
i think removing a bit of the fire reduces the chance of this wrong crash
m
Ok, then it might be very very difficult to do chunk regen without shutting down the server.
If we get the chunk regen, then the tile tick list will almost certainly be so messed up that the server shuts down at the beginning of the next tile tick phase
We should look into whether this can be avoided, but it might turn out it cannot be avoided
If we can't avoid a server shutdown, then this can't really be used to get async lines directly
but we might be able to take a chunk that was already populated, and make it unpopulated again, and save as unpopulated to disk
And I think the crash I got there is the crash we want to happen on the async thread.
The way I understand it is that both the main thread and the async thread schedule tile ticks, and if you do that you can get the above crash, and if the async thread gets this crash it causes the chunk regen
But that crash also messes up the tile tick list, so in the next tile tick phase after the chunk regen happened, the main thread crashes and the server shuts down
c
What if we try to get no tile ticks on the main thread, and async load two chunks so the server doesnt shut down
what is really difficult about this is that after one async load, the other unload almost always gets cancelled by the thread
i think i must use very little glass
let me try without mob spawning
it worked
i got 2 async loads
but no chunk regen
m
I think you will still get a crash after the chunk regen
because what's shutting down your server is not the crash when the tile ticks get scheduled
what's shutting down the server is that the tile tick list gets severely messed up, and the main thread checks whether it's messed up once per gtick
We should definitely try doing it with 2 async loads
but there is a good chance that getting a chunk regen from that still shuts down the server in the next tile tick phase
c
i did get the double async load
m
Yes. But when you get the chunk regen, I expect it to shut down the server, with like 80% confidence
The server only keeps running if this async tile tick scheduling crash does not mess up the tile tick list enough for the "tile tick list out of synch" crash
c
maybe it would be better to have just one tile tick saved or something
m
I doubt it. I think the best thing to work on here is a setup that regens a chunk and shuts down the server, but makes it so the chunk gets saved to disk as unpopulated when the server shuts down
I would definitely first test with a lot of tile ticks, so we get a good idea what the success chances even are
I don't think you get good chances with just a single tile tick
c
with two async chunk loads i havent gotten the regen even once
m
In my unload double swapper I did get chunk regen purely from async threads loading a chunk with just a few hundred tile ticks
but it was probably 5 unload chunk swaps at once or something
With your two async loads, the question is how far apart those async loads are in terms of timing
Maybe one of the async threads completely finishes loading the chunk before the other one even started.
I don't know your setup, but with void's synchronized method the precise timing of the async chunk load is quite difficult to control.
this is the setup
m
We would need to somehow profile whether the time where the two async threads load in the tile ticks overlap
2 Views