Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-224729

Partially generated chunks are not saved in some situations

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • None
    • 1.16.5, 21w17a, 1.17.1, 21w40a, 21w41a, 1.18 Pre-release 4, 1.18 Pre-release 5, 1.18 Pre-release 6, 1.18 Pre-release 7, 1.18.2, 1.19 Pre-release 1
    • None
    • Confirmed
    • Chunk loading, World generation
    • Important
    • Platform

      There are two codepaths that invoke saving of chunks.
      1. ThreadedAnvilChunkStorage.tryUnloadChunk(...) which saves chunks that are no longer kept alive by any chunk ticket, before finally unloading them from the world.
      2. ThreadedAnvilChunkStorage.save(flush) which saves chunks periodically and upon closing the world.

      net.minecraft.server.world.ThreadedAnvilChunkStorage.java
      protected void save(boolean flush) {
        if (flush) {
           ...
        } else {
           this.chunkHolders.values().stream().filter(ChunkHolder::isAccessible).forEach((chunkHolder) -> {
              Chunk chunk = (Chunk)chunkHolder.getSavingFuture().getNow((Object)null);
              if (chunk instanceof ReadOnlyChunk || chunk instanceof WorldChunk) {
                 this.save(chunk);
                 chunkHolder.updateAccessibleStatus();
              }
           });
        }
      }
      

      This code saves all accessible chunks, i.e., chunks that were in the BORDER status at some point since the last save, periodically (when flush is false) and upon closing the world (when flush is true). The second case is similar (and hence not included in the code snippet) but additionally waits for pending futures in the accessible chunks.

      The point is now that neither of these cases covers chunks that are still kept alive by chunk tickets when closing the world, but do not have a sufficient ticket level to be BORDER chunks. Those are chunks that are only partially generated, but can already contain features and light propagations from adjacent chunks. Not saving these chunks will hence lead to corrupted features and broken light at chunk borders.
      In the following I will present some concrete examples where chunk tickets can keep such chunks alive and will hence prevent saving. Some other ticket types can most certainly lead to the same effect.

      1. Spawn chunks

      The first example uses START tickets created on spawn chunks (the same works with FORCED tickets), which are not removed upon unloading the world and hence will keep some chunks alive.

      • Create a new world with render distance 2 (e.g., with seed -4530634556500121041)
      • Quit and reload directly after generating the world
      • One can now turn up the render distance again
      • Observe that features between the 11th and 12th chunk away from the spawn point are broken (World spawn loads chunks in a radius of 11 into BORDER status)

      Due to the low render distance, chunks in distance 12 from the spawn point are not requested into BORDER status, but still kept from unloading due to the START ticket, hence triggering the described issue.

      2. Overload the lighting engine

      The second example uses LIGHT tickets created during worldgen to keep chunks alive. While these tickets will eventually be removed when the worldgen is finished, the goal here is to overload the lighting engine so that these generations steps will not be finished (and hence the tickets will not be removed) in time before the flush save completes. Note that the flush save only waits for accessible chunks to finish any remaining futures, but not for partially generated chunks.
      Note that this behavior is quite random and hence the following steps might require several attempts for reproduction or may not work at all, depending on the setup. Nevertheless, I think this example is still of practical relevance as described in the final section.
      In order to stress the lighting engine we can try the following steps

      • Create an AMPLIFIED world with render distance 32
      • Fly a few chunks in one direction and then close the world immediately
      • Repeat the previous step several times, always flying in the same direction
      • After repeating for a few times, revisit the generated chunks again and see if some corrupted features or light glitches occur.

        (Note that there are also some lighting glitches on the chunk border)

      Closing the world immediately after flying a few chunks does not give the lighting engine enough time to finish its jobs before unloading the world (at least sometimes), hence causing the described issue.

      3. Player tickets

      As pointed out by PG85, PLAYER tickets can cause this issue very deterministically (comparable to the START tickets) and are hence probably the most common and severe case. While PLAYER tickets are explicitly removed upon shutting down the world by disconnecting all players before saving, this ticket removal does involve some threading and hence might not be completed before the saving executes, hence keeping all chunks loaded by players alive during saving and consequently triggering the issue.

      4. Interacting with the world

      Every world interaction, or more precisely every call to World.getChunk(...), creates an UNKNOWN chunk ticket with the intent to keep the chunk loaded and hence valid until the end of the tick. Every ticking entity and every player creates such a ticket at its location every tick, e.g., due to movement operations querying the chunk. These UNKNOWN tickets are in fact not cleared at the end of the current tick, but at the start of the next one. Consequently, these UNKNOWN tickets from the last tick are not purged upon shutting down the world and are hence still present during saving, prohibiting unloading and saving of some chunks (in contrast, PLAYER tickets are explicitly removed upon shutting down the world by disconnecting all players).
      This does not seem to be an issue on high render distances, presumably because there are no entities far away from the player, at least not in the very last tick, and hence no such UNKNOWN tickets are created away from the player, whereas the PLAYER ticket itself has a much higher range and hence dominates these UNKNOWN tickets. Hence the UNKNOWN tickets only affect chunks that were already accessible due to the PLAYER ticket and hence do not trigger the saving issue.
      However, this argument does not work for small render distances, e.g., render distance 2, where theĀ  UNKNOWN tickets do keep chunks alive that were not yet accessible, hence causing the saving issues. Indeed, the issue does show up deterministically when exploring new chunks with a render distance of 2 and reloading the world every few chunks.

      Remarks

      I think the issue described here is a good candidate for explaining MC-125007 (respectively MC-188086).
      I am not sure, though, to which extent it is related to MC-214793. It again seems to be a good explanation for the corrupted features that seem to occur in conjunction with the dark chunks. However, it fails to explain why these chunks are completely dark; it only explains missing light propagations at the chunk borders, since these are discarded upon saving. So, it looks like some other unknown issue is interfering here as well.

        1. 2021-06-20_19.16.16.png
          2021-06-20_19.16.16.png
          1.05 MB
        2. image-2021-05-02-23-10-32-349.png
          image-2021-05-02-23-10-32-349.png
          2.89 MB
        3. image-2021-05-02-23-50-06-769.png
          image-2021-05-02-23-50-06-769.png
          3.24 MB
        4. image-2021-05-18-17-50-02-149.png
          image-2021-05-18-17-50-02-149.png
          56 kB
        5. image-2021-05-18-18-50-38-617.png
          image-2021-05-18-18-50-38-617.png
          521 kB
        6. image-2021-06-05-18-17-16-754.png
          image-2021-06-05-18-17-16-754.png
          984 kB
        7. Inked2021-06-20_19.18.11_LI.jpg
          Inked2021-06-20_19.18.11_LI.jpg
          4.67 MB
        8. java_19PPkmaU8x.png
          java_19PPkmaU8x.png
          559 kB

            Unassigned Unassigned
            PhiPro Philipp Provenzano
            Votes:
            35 Vote for this issue
            Watchers:
            23 Start watching this issue

              Created:
              Updated:
              CHK: