Skip to content

Suggestion: Add ticket on player join to avoid chunk load-unload-load cycle #29

@Techirion

Description

@Techirion
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: UnnamedPlayer <info@example.org>
Date: Wed, 12 Mar 2025 22:03:42 +0100
Subject: [PATCH] Add ticket on player join to avoid chunk load-unload-load
 cycle


diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
index dd2509996bfd08e8c3f9f2be042229eac6d7692d..bc31c8d4d362fb2633bfc57b3a15a89a1c0ef2a0 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
@@ -31,6 +31,7 @@ import net.minecraft.server.level.ServerLevel;
 import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.server.level.TicketType;
 import net.minecraft.server.network.PlayerChunkSender;
+import net.minecraft.util.Unit;
 import net.minecraft.world.level.ChunkPos;
 import net.minecraft.world.level.GameRules;
 import net.minecraft.world.level.chunk.ChunkAccess;
@@ -47,6 +48,7 @@ public final class RegionizedPlayerChunkLoader {
 
     public static final TicketType<Long> PLAYER_TICKET         = TicketType.create("chunk_system:player_ticket", Long::compareTo);
     public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5 * 20);
+    public static final TicketType<Unit> PLAYER_JOIN           = TicketType.create("chunk_system:player_join", (a, b) -> 0, 5 * 20);
 
     public static final int MIN_VIEW_DISTANCE = 2;
     public static final int MAX_VIEW_DISTANCE = 32;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 5a4960fdbd97d830ac79845697eea9372c48a13b..c73e935295ee0859d5d190347a0259c157305471 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -312,6 +312,13 @@ public abstract class PlayerList {
         // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
         // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
         player.supressTrackerForLogin = true;
+        // Paper start - rewrite chunk system
+        serverLevel.moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(
+            ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PLAYER_JOIN,
+            player.chunkPosition(),
+            ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.TICK_TICKET_LEVEL,
+            net.minecraft.util.Unit.INSTANCE);
+        // Paper end - rewrite chunk system
         serverLevel.addNewPlayer(player);
         this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player);
         // Paper end - Fire PlayerJoinEvent when Player is actually ready

From: PaperMC/Paper#11398

Reasoning:

If a player joins and is located in unloaded chunk at the time of join, server would try to load that chunk, then immediately unload it (and save it) and load it again.

Which is unnecessary performance hit, furthermore it seems like in some instances (unable to replicate this reliably but can see dataloss) second ChunkLoadEvent is fired before "dummy" ChunkUnloadEvent is fired, resulting in dataloss for plugins that load/unload data on such events.

The patch above fixes the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions