Skip to content

Chunks forced by other mods or /forceload are removed every tick #36

Closed

Description

@Smalldero

When another mod or the built‑in /forceload command forces chunks to load via setChunkForced method, this mod immediately un‑forces them on every game tick—effectively overriding all external chunk‑loading sources.

Reproduction Steps

  1. Force-load a chunk using /forceload add ~ ~
  2. Verify the chunk is loaded with /forceload query ~ ~
  3. Move to another chunk and create the chunk loaded minecart from this mod
  4. Return to the original forced chunk and run /forceload query ~ ~ → the chunk is no longer loaded

Expected Behavior

The mod should only remove chunks that it forced itself - leaving chunks loaded by other mods or by /forceload intact.

Fix for version 1.21.5

Add the following to the ScriptsChunkLoadersMod Class:

+ public static final ChunkTicketType CUSTOM_TICKETTYPE =  Registry.register(Registries.TICKET_TYPE, ScriptsChunkLoadersMod.MODID, new ChunkTicketType(0L, true, ChunkTicketType.Use.LOADING_AND_SIMULATION));

Then change the following in the ChunkLoaderManager Class:

private void updateChunkLoaders(ServerWorld world) {
      var currentChunks = calculateLoadedChunks(world);

      // TODO: This can probably be optimized. We're looping over the same range twice since there usually is an
      //  overlap between the current chunks and the loaded chunks.

      final LongSet loadedChunks = world.getForcedChunks();

      currentChunks.forEach(chunkPos -> {
          long longPos = chunkPos.toLong();

          if (!loadedChunks.contains(longPos)) {
              // Load chunk
-             world.setChunkForced(chunkPos.x, chunkPos.z, true);
+            var ticket = new ChunkTicket(ScriptsChunkLoadersMod.CUSTOM_TICKETTYPE, ServerChunkLoadingManager.FORCED_CHUNK_LEVEL);
+            world.getChunkManager().addTicket(ticket, chunkPos);
          }
      });

      loadedChunks.forEach(longPos -> {
          var chunkPos = new ChunkPos(longPos);
          if (!currentChunks.contains(chunkPos)) {
              // Unload chunk
-             world.setChunkForced(chunkPos.x, chunkPos.z, false);
+            world.getChunkManager().removeTicket(ScriptsChunkLoadersMod.CUSTOM_TICKETTYPE, chunkPos, 2);
          }
      });
  }

All chunks loaded by this mod are now registered under the custom ChunkTicketType. When these chunks are unloaded, they are only removed from that ChunkTicketType and all other loaded chunks remain untouched.

Version 1.21.9

The following changes in the 1.21.9 version of the game in the ScriptsChunkLoadersMod Class:

- public static final ChunkTicketType CUSTOM_TICKETTYPE =  Registry.register(Registries.TICKET_TYPE, ScriptsChunkLoadersMod.MODID, new ChunkTicketType(0L, true, ChunkTicketType.Use.LOADING_AND_SIMULATION));
+ public static final ChunkTicketType CUSTOM_TICKETTYPE =  Registry.register(Registries.TICKET_TYPE, ScriptsChunkLoadersMod.MODID, new ChunkTicketType(ChunkTicketType.NO_EXPIRATION, ChunkTicketType.SERIALIZE | ChunkTicketType.FOR_LOADING | ChunkTicketType.FOR_SIMULATION | ChunkTicketType.RESETS_IDLE_TIMEOUT));

Activity

  1. self-assigned this
    on Apr 4, 2026
  2. github-actions commented on Apr 5, 2026

    @github-actions
    Contributor

    🎉 This issue has been resolved in version 0.8.1 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

  3. github-actions commented on Apr 8, 2026

    @github-actions
    Contributor

    🎉 This issue has been resolved in version 0.8.1 🎉

    The release is available on GitHub release

    Your semantic-release bot 📦🚀

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions