fix gamemode player join
This commit is contained in:
@@ -30,10 +30,11 @@ public final class DimensionGameModeManager {
|
||||
|
||||
private final Map<String, String> configured = new LinkedHashMap<>();
|
||||
private final Map<UUID, String> lastDimension = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Integer> joinEnforcementTicks = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Integer> joinSyncTicks = new ConcurrentHashMap<>();
|
||||
|
||||
private static final int JOIN_ENFORCEMENT_DURATION_TICKS = 100; // 5 detik
|
||||
private static final int JOIN_ENFORCEMENT_INTERVAL_TICKS = 5; // 0.25 detik
|
||||
// Koreksi post-login pada +1, +5, dan +20 tick. JOIN sendiri melakukan
|
||||
// apply immediate (stage +0) sebelum antrean ini dimulai.
|
||||
private static final int JOIN_SYNC_FINAL_TICK = 20;
|
||||
|
||||
public boolean load() {
|
||||
if (!Files.exists(PATH)) {
|
||||
@@ -88,28 +89,55 @@ public final class DimensionGameModeManager {
|
||||
applyConfiguredMode(player, dimension, true);
|
||||
}
|
||||
|
||||
Integer remaining = joinEnforcementTicks.get(uuid);
|
||||
if (remaining != null) {
|
||||
if (remaining <= 0) {
|
||||
joinEnforcementTicks.remove(uuid);
|
||||
Integer elapsed = joinSyncTicks.get(uuid);
|
||||
if (elapsed != null) {
|
||||
int next = elapsed + 1;
|
||||
if (next == 1 || next == 5 || next == JOIN_SYNC_FINAL_TICK) {
|
||||
applyJoinSyncStage(player, dimension, next);
|
||||
}
|
||||
if (next >= JOIN_SYNC_FINAL_TICK) {
|
||||
joinSyncTicks.remove(uuid);
|
||||
} else {
|
||||
if (remaining % JOIN_ENFORCEMENT_INTERVAL_TICKS == 0) {
|
||||
applyConfiguredMode(player, dimension, true);
|
||||
}
|
||||
joinEnforcementTicks.put(uuid, remaining - 1);
|
||||
joinSyncTicks.put(uuid, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selama beberapa detik pertama setelah login, gamemode dikoreksi berulang.
|
||||
* Ini sengaja dibuat sebagai stabilization window karena vanilla player-data
|
||||
* maupun mod lain dapat menerapkan state player sedikit lebih lambat dari event JOIN.
|
||||
* Setelah window selesai, player tidak terus-menerus dipaksa kecuali berpindah dimension.
|
||||
* Menjadwalkan koreksi gamemode singkat sesudah JOIN. Initial world placement
|
||||
* tetap immediate; hanya gamemode yang dikoreksi pada +1, +5, dan +20 tick.
|
||||
*/
|
||||
public void beginJoinEnforcement(UUID uuid) {
|
||||
joinEnforcementTicks.put(uuid, JOIN_ENFORCEMENT_DURATION_TICKS);
|
||||
joinSyncTicks.put(uuid, 0);
|
||||
}
|
||||
|
||||
private void applyJoinSyncStage(ServerPlayer player, String dimension, int tick) {
|
||||
String configuredMode = configured.get(normalizeDimension(dimension));
|
||||
if (configuredMode == null) return;
|
||||
|
||||
GameType before = player.gameMode.getGameModeForPlayer();
|
||||
applyConfiguredMode(player, dimension, false);
|
||||
GameType after = player.gameMode.getGameModeForPlayer();
|
||||
|
||||
LobbyLog.state(
|
||||
"JOIN GAMEMODE SYNC",
|
||||
"Player: " + player.getName().getString(),
|
||||
"Dimension: " + dimension,
|
||||
"Expected: " + configuredMode,
|
||||
"Before: " + gameTypeName(before),
|
||||
"After: " + gameTypeName(after),
|
||||
"Stage: +" + tick + " tick" + (tick == JOIN_SYNC_FINAL_TICK ? " / final" : "")
|
||||
);
|
||||
}
|
||||
|
||||
private static String gameTypeName(GameType type) {
|
||||
if (type == null) return "unknown";
|
||||
if (type == GameType.SURVIVAL) return "survival";
|
||||
if (type == GameType.CREATIVE) return "creative";
|
||||
if (type == GameType.ADVENTURE) return "adventure";
|
||||
if (type == GameType.SPECTATOR) return "spectator";
|
||||
return type.toString().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +154,7 @@ public final class DimensionGameModeManager {
|
||||
|
||||
public void onDisconnect(UUID uuid) {
|
||||
lastDimension.remove(uuid);
|
||||
joinEnforcementTicks.remove(uuid);
|
||||
joinSyncTicks.remove(uuid);
|
||||
}
|
||||
|
||||
public String get(String dimension) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.gamerules.GameRule;
|
||||
import id.dartokloning.lobbysystems.mixin.ServerPlayerGameModeAccessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -975,17 +974,11 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
player.gameMode.setLevel(target);
|
||||
player.snapTo(c.lobbyX, c.lobbyY, c.lobbyZ, c.lobbyYaw, c.lobbyPitch);
|
||||
|
||||
if (c.manageDimensionGamemode) {
|
||||
String configuredMode = DIMENSION_GAMEMODES.get(c.lobbyDimension);
|
||||
GameType gameType = DimensionGameModeManager.parseMode(configuredMode);
|
||||
if (gameType != null && player.gameMode.getGameModeForPlayer() != gameType) {
|
||||
GameType previousMode = player.gameMode.getGameModeForPlayer();
|
||||
ServerPlayerGameModeAccessor accessor = (ServerPlayerGameModeAccessor) player.gameMode;
|
||||
accessor.lobbySystems$setPreviousGameModeForPlayer(previousMode);
|
||||
accessor.lobbySystems$setGameModeForPlayer(gameType);
|
||||
gameType.updatePlayerAbilities(player.getAbilities());
|
||||
}
|
||||
}
|
||||
// Jangan mengubah gamemode internal pada fase pre-login. Vanilla masih
|
||||
// memulihkan gamemode tersimpan selama lifecycle login. Jika kita mengubah
|
||||
// field internal di sini, server dapat menganggap mode sudah benar sehingga
|
||||
// ServerPlayer#setGameMode() sesudah JOIN tidak mengirim update ke client.
|
||||
// Gamemode Lobby disinkronkan setelah JOIN oleh DimensionGameModeManager.
|
||||
|
||||
LobbyLog.state(
|
||||
"PLAYER PRE-LOGIN PLACEMENT",
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package id.dartokloning.lobbysystems.mixin;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
/**
|
||||
* Akses internal gamemode hanya untuk fase pre-login, sebelum connection player siap.
|
||||
* Kita tidak memanggil ServerPlayer#setGameMode di titik ini karena method tersebut
|
||||
* dapat mencoba mengirim update network terlalu dini.
|
||||
*/
|
||||
@Mixin(ServerPlayerGameMode.class)
|
||||
public interface ServerPlayerGameModeAccessor {
|
||||
@Accessor("gameModeForPlayer")
|
||||
void lobbySystems$setGameModeForPlayer(GameType gameType);
|
||||
|
||||
@Accessor("previousGameModeForPlayer")
|
||||
void lobbySystems$setPreviousGameModeForPlayer(GameType gameType);
|
||||
}
|
||||
Reference in New Issue
Block a user