Skip to content

Instantly share code, notes, and snippets.

@Drovolon
Created August 11, 2019 14:36
Show Gist options
  • Save Drovolon/24bfaae00d57e7a8ca64b792e14fa7c6 to your computer and use it in GitHub Desktop.
Save Drovolon/24bfaae00d57e7a8ca64b792e14fa7c6 to your computer and use it in GitHub Desktop.
An overview of chunk loading mechanics in Minecraft 1.14, tested empirically in 1.14.4.

1.14.x Chunk Loading

Chunk loading operates differently in 1.14 than in previous Minecraft versions. This document is intended to be an overview of the 1.14 system.

In 1.14, chunk loading starts with tickets. A ticket is:

  • a ticket type
  • a load level
  • optionally, a time-to-live

Each chunk has an ordered set of tickets associated with it. All loaded chunks ultimately stem from a ticket of some type.

Load Levels

Load levels range from 22 to 44 in regular gameplay with only 22 to 33 being relevant. Technically, lower load levels than 22 are possible (but not without a modded game). Load levels above 44 are not possible.

For a given chunk, only its lowest-level ticket matters. (Lower levels = a wider range of loading.)

Level Types

The load level determines which gameplay features, roughly, are active in a given chunk. There are four load level types:

  • ENTITY_TICKING - Entity ticking occurs in load levels 31 and below. This is the main load level type players will experience. All game features are active here, including entity processing (mobs wandering, spawning and despawning, etc.)
  • TICKING - Ticking occurs at load level 32. At this load level, all game features run as normal, except mobs are not being processed. (They will not wander, despawn, etc.) Redstone componentry runs as normal in these chunks.
  • BORDER - Border occurs at load level 33. These chunks are "loaded" but typical game features do not work in them. Redstone componentry does not function, command blocks don't tick, entities don't tick, etc. However, mobs in border chunks do count towards the mob cap, for example.
  • INACCESSIBLE - Load levels 34 and up. These are not loaded chunks in any meaningful sense. However, world generation occurs on these chunks. (I think.)

Because inaccessible (34+) levels are not relevant in normal gameplay, they will be omitted from the rest of this document (mostly to keep pictures to a manageable size)—but they are still at play, technically.

Level Propagation

Load levels propagate to neighboring chunks. For each ticket, the load level "flood fills" outward from the ticket, increasing the chunk's load level by one for each step outwards, until the max level (44) is reached. For example, if a chunk has a ticket with level = 30, the neighboring chunks will look like:

   34   34   34   34   34   34   34   34   34
   34   33   33   33   33   33   33   33   34
   34   33   32   32   32   32   32   33   34
   34   33   32   31   31   31   32   33   34
   34   33   32   31   30   31   32   33   34
   34   33   32   31   31   31   32   33   34
   34   33   32   32   32   32   32   33   34
   34   33   33   33   33   33   33   33   34
   34   34   34   34   34   34   34   34   34

From above, 34 will not be loaded, 33 will be border (no ticking), 32 will be ticking (but no mob processing), and 31 & 30 will be loaded chunks with full entity processing.

NOTE: There example above contains one chunk ticket at level 30. The other loaded chunks are loaded as a result of that one level 30 ticket. Corollary: lower ticket levels correspond to a larger area loaded.

Or in diagram form:

    -    -    -    -    -    -    -    -    -
    -   BD   BD   BD   BD   BD   BD   BD    -
    -   BD   TI   TI   TI   TI   TI   BD    -
    -   BD   TI   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   TI   BD    -
    -   BD   TI   TI   TI   TI   TI   BD    -
    -   BD   BD   BD   BD   BD   BD   BD    -
    -    -    -    -    -    -    -    -    -

where - is unloaded, BD is border, TI is ticking, and ET is entity ticking.

When tickets have overlapping ranges of effect, the lowest load level wins. Imagine ticket levels "flood-filling" out from tickets. They don't combine or stack.

Coincidentally, level 30 is the same level used by entities going through portals. Thus, for example, if you sent an item through a portal, a 3 by 3 grid of entity processing chunks will be used on the other side, with ticking and border chunks surrounding it.

Implications

Mob Switch Positioning

If one wanted to build a mob switch where entities were kept loaded but not being processed (so no load on the server), ideally you'd put them in either ticking or border chunks. Portals can be used to create arbitrary permanent chunk loading by periodically sending items through them. You'd place the mobs for the mob switch two or three chunks away from the portal chunk itself, so that the mobs would end up being in a ticking or border chunk.

No Chunk Promotion

Note that entity processing chunks are not determined by the number of chunks loaded around them, as was the case in earlier versions of Minecraft. If you had a modded game that allowed you to create arbitrary ticket levels, you could have an arbitrarily-large grid of border chunks and none of them would process entities.

Even in regular gameplay it is possible to have non-entity-ticking chunks surrounded by loaded chunks. For example, imagine two precisely positioned portals sending items through each other periodically, such that their tickets in the nether are adjacent but not overlapping:

-    -    -    -    -    -    -    -    -    -    -    -    -    -    -    - 
-   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD    -
-   BD   TI   TI   TI   TI   TI   BD   BD   TI   TI   TI   TI   TI   BD    -
-   BD   TI   ET   ET   ET   TI   BD   BD   TI   ET   ET   ET   TI   BD    -
-   BD   TI   ET   ET   ET   TI   BD   BD   TI   ET   ET   ET   TI   BD    -
-   BD   TI   ET   ET   ET   TI   BD   BD   TI   ET   ET   ET   TI   BD    -
-   BD   TI   TI   TI   TI   TI   BD   BD   TI   TI   TI   TI   TI   BD    -
-   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD    -
-    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -

Note the four chunk wide strip of ticking & border chunks in the middle. These are not promoted to entity-processing chunks.

Overlapping Ticket Ranges

Take the above example of two level 30 tickets positioned exactly far enough away to not interfere with each other—and move them two chunks closer, so that their outer range overlaps:

   34   34   34   34   34   34   34   34   34   34   34   34   34   34  
   34   33   33   33   33   33   33   33   33   33   33   33   33   34  
   34   33   32   32   32   32   32   32   32   32   32   32   33   34  
   34   33   32   31   31   31   32   32   31   31   31   32   33   34  
   34   33   32   31   30   31   32   32   31   30   31   32   33   34  
   34   33   32   31   31   31   32   32   31   31   31   32   33   34  
   34   33   32   32   32   32   32   32   32   32   32   32   33   34  
   34   33   33   33   33   33   33   33   33   33   33   33   33   34  
   34   34   34   34   34   34   34   34   34   34   34   34   34   34  

Notice that there are no more border (level 33) chunks in the middle as there were above. What chunks would have been level 33 on one side were "overwritten" by the level 32 chunks from the other side.

Idle Timeout

Each dimension has its own idle timeout. If a dimension has players in it, or there are forceloaded chunks in it, the idle timeout is effectively disabled. Otherwise, every time an entity enters or leaves that dimension, the idle timeout is reset to 0 again. If it reaches 300 game ticks without being reset, the server stops processing certain actions in that dimension until the timeout is reset (player joins, entity changes dimensions, or a chunk is marked forceloaded)

  • No entities will be ticked/processed
  • The ender dragon fight will not be updated
  • Global entities will not be ticked (not sure what these are - just lightning?)

Ticket Types

These ticket types are currently implemented in Minecraft:

forced

The /forceload command allows you to set "forced" tickets with a load level of 31 on arbitrary chunks. The set of forceloaded chunks is persisted to the world data files by the server, and when the server starts back up, it sets the forced ticket on those chunks again. (In other words, /forceload tickets persist across restarts.)

The load level of 31 is fixed and results in the following chunk load layout:

   34   34   34   34   34   34   34
   34   33   33   33   33   33   34
   34   33   32   32   32   33   34
   34   33   32   31   32   33   34
   34   33   32   32   32   33   34
   34   33   33   33   33   33   34
   34   34   34   34   34   34   34

or in pretty-printed form,

    -    -    -    -    -    -    -
    -   BD   BD   BD   BD   BD    -
    -   BD   TI   TI   TI   BD    -
    -   BD   TI   ET   TI   BD    -
    -   BD   TI   TI   TI   BD    -
    -   BD   BD   BD   BD   BD    -
    -    -    -    -    -    -    -

So, the chunk that is forceloaded will be entity-ticking, but it'll be surrounded by regular ticking and border chunks as usual.

If you want to build a mob switch with /forceload, you should place the mobs in one of the ticking or border chunks. In other words, forceload a chunk one or two chunks away from where the mobs will be placed.

If you're willing to accept commands, /forceload is far and away the best way to, well, force load chunks. If not, look into "portal" ticket below.

portal

Portal tickets are set when an entity passes through a nether portal. They are sent on the chunk containing the matching portal. (If you throw an item through a nether portal in the overworld, the chunk containing the matching portal in the nether will have a ticket set on it.) They have a load level of 30.

Portal tickets do not have a TTL in the ticket system itself, but the portal system will remove the associated ticket if the portal goes unused for 300 game ticks (under no lag equivalent to 15 seconds).

Diagrams for the chunk loading layout can be found above in the load level section, as level 30 was used as an example there. In summary: a 3 by 3 grid of entity processing chunks with a strip of ticking and border chunks around it.

Periodically sending items through a portal, and sending items back again from the other dimension, is probably the best way to create a perma-loaded set of chunks (in both the nether and overworld) without using commands (see /forceload above).

Portal Caching & Item Alignment
Technical details about portal caching; skip if uninterested.
When an entity goes through a portal, the game needs to know the matching portal in the other dimension. Portal tickets happen to double-dip and act as a kind of cache for the matching portal's position.

For traveling from the overworld to the nether, the matching portal's coordinates are cached under the key (floor(entity.x/8), floor(entity.z/8)), in other words the overworld coordinates mapped into the nether. If another entity goes through the same portal, the same floor and division is done. Ideally, those coordinates would match whatever is in the cache so that an expensive portal lookup doesn't need to happen again. Since most portals are less than 8 blocks wide, division and flooring means that even subtly different entity coordinates are fine. They all get "squished": the overworld coordinates (7.9999,0) and (0,0) both get mapped to (0,0) for the cache key, so two items going in at those two different overworld positions use the same cached portal.

The other direction is problematic. From the nether to the overworld, the coordinates are cached under the key (floor(entity.x*8), floor(entity.z*8)). Here, even minute differences in entity positions are a problem. For example, (0,0) in the nether maps to (0,0) in the overworld, but (0.2,0) ends up mapping to (1,0)! Since they map differently, those two entities going through the portal will each cause portal searches, and that's bad news for server performance.

(How much bad news? A simple dropper pointed at a nether portal leading to the overworld, shooting items periodically, gives huge noticeable lag spikes very frequently. Enough that the game isn't really playable.)

What the technical stuff above means: if you need to keep an overworld chunk loaded, and you want to do that by shooting items through a nether portal, you'll need to align items from a dropper (for example), because otherwise having items shooting blindly through the portal will lag the server like crazy from their positional differences.

player

The "player" ticket has a load level of 31. It's added by the server to all chunks in a grid around each player (with the size of the grid being the server render distance). By default, this means a 10 by 10 grid will be entity processing, with a strip of regular ticking chunks around that, with a strip of border chunks around that.

At least, that's what I've observed in practice. There's some complex logic around "throttling" player tickets. Your Mileage May Vary.

start

The "start" ticket is placed in the world spawn chunk at a load level of 22, the lowest load level used in regular gameplay. This results in spawn chunks being permanently loaded, with a 19 by 19 grid of entity processing chunks (note: see Idle Timeout above for a caveat):

   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34
   34   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   34
   34   33   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   33   34
   34   33   32   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   32   33   34
   34   33   32   31   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   31   32   33   34
   34   33   32   31   30   29   29   29   29   29   29   29   29   29   29   29   29   29   29   29   30   31   32   33   34
   34   33   32   31   30   29   28   28   28   28   28   28   28   28   28   28   28   28   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   27   27   27   27   27   27   27   27   27   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   26   26   26   26   26   26   26   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   25   25   25   25   25   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   24   24   24   24   24   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   24   23   23   23   24   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   24   23   22   23   24   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   24   23   23   23   24   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   24   24   24   24   24   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   25   25   25   25   25   25   25   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   26   26   26   26   26   26   26   26   26   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   27   27   27   27   27   27   27   27   27   27   27   28   29   30   31   32   33   34
   34   33   32   31   30   29   28   28   28   28   28   28   28   28   28   28   28   28   28   29   30   31   32   33   34
   34   33   32   31   30   29   29   29   29   29   29   29   29   29   29   29   29   29   29   29   30   31   32   33   34
   34   33   32   31   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   30   31   32   33   34
   34   33   32   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   31   32   33   34
   34   33   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   32   33   34
   34   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   33   34
   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34   34

or in pretty-printed form:

    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -
    -   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD    -
    -   BD   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   ET   TI   BD    -
    -   BD   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   TI   BD    -
    -   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD   BD    -
    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -

dragon

The "dragon" ticket has a load level of 24. It's added at the beginning of the Ender Dragon fight to the (0, 0) chunk in The End, and it's removed when there are no more players participating in the fight.

post-teleport

A "post-teleport" ticket is added when an entity is teleported, either by the /tp command, the /spreadplayers command, or by going through an end portal. The ticket is added to the chunk associated with the new position. For the /tp command, it has a load level of 32 (ticking), whereas for /spreadplayers and the end portal, it has a load level of 33 (border). In both cases, the ticket has a time-to-live of five (5) game ticks. After five ticks, it will be automatically removed by the ticket system.

unknown

I haven't put much investigation into the "unknown" ticket. It's the ticket used by the game when an arbitrary piece of game code calls "getChunk". If the game code says the chunk should be loaded, it places this ticket on that chunk (after possibly creating it). It has a time-to-live of one (1) game tick.

The load level depends on what kind of chunk the game attempts to load (a fully world-generated chunk or not). It will be at least 33 (border), but in many cases it could beyond 33, which will involve world-gen in some way.

As an example, when generic mob wandering AI is executing, it asks if a certain block has a solid top surface. As part of that check, that chunk will have an "unknown" ticket with level 33 placed on it.

light

I'm not sure how this one works. It seems to be world-gen-related.

@NicoNekoDev
Copy link

Okay, the chunks go unloaded, but to where? The chunks can't be really unloaded (from memory) because those chunks need to be saved (to disk). What actually happens to unloaded chunks when the world is actually saved? Or the chunks go directly saved (to disk) when are unloaded?

@Drovolon
Copy link
Author

Drovolon commented Jan 9, 2021

@nicuch, I haven't looked at the code in a really long time, but from what I recall, "unloaded" is sort of a misnomer. It's more about which chunks are ticked, less about it being in memory or not.

I do remember seeing the code that saves chunk state to disk, but I can't remember what event(s) trigger that, sorry. I know it's done server-wide at least periodically, though whether it's time or tick based I'm not sure, and there are server commands to force a write to disk and also to toggle the periodic save. (That's how a proper Minecraft server backup works, as far as I know: disable periodic saves, force save to disk, backup the files somehow, then re-enable periodic saves.) Based on that I'd guess that saving chunks to disk probably isn't something that happens outside of that periodic process, but again, that's just a guess on my part.

Sorry I can't provide much more clarity. It's not too difficult to get the decompiled source working in an IDE if you're handy with Java.

@NicoNekoDev
Copy link

@Drovolon, that means that "loading" is sort of misnomer as well, as this should be called "chunk propagation and ticking" not "chunk loading".
For the decompilation part... pretty much I don't know how to exactly do it (tried a couple of times with no success).
Anyway, thanks for answering!

@Drovolon
Copy link
Author

Drovolon commented Jan 9, 2021

Yeah, perhaps so. This doc definitely doesn't describe how loading/unloading (from disk) works. Thing is, the "loading" terminology is pretty embedded in the community now, and even in the game too with the "/forceload" command (which just creates a ticket that persists across server reboots). 🤷‍♂️

As far as getting IDE support, you can try to get a Fabric plugin project working in IntelliJ IDEA — should be guides online for that, I think? — and then browsing the deobfuscated Minecraft JAR that gets pulled as a dependency. That may be the most straightforward way.

Otherwise, you can use Yarn to get a deobfuscated JAR, the unzip the JAR and open the source files as a project in IntellIiJ, I think. I remember doing this to get better IDE features working, but I also remember it being rather laborious to track down various dependencies to make IntelliJ happy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment