diff --git a/README.md b/README.md index c6796bc..aed927c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Lobby Systems v1.5.6 — Fabric 26.2 +# Lobby Systems v1.5.7 — Fabric 26.2 -Server-side lobby routing untuk Minecraft 26.2/Fabric. v1.5.6 mengubah proses login agar Lobby menjadi initial dimension player sebelum world awal dikirim ke client, sehingga tidak ada lagi teleport join yang ditunda atau "persinggahan" di dimension terakhir. +Server-side lobby routing untuk Minecraft 26.2/Fabric. v1.5.7 mempertahankan initial Lobby placement sebelum world awal dikirim ke client dan menjadikan Lobby Systems sebagai pengelola gamemode per-dimension yang selalu aktif. -**v1.5.6 login placement fix:** player data tetap dibaca untuk menyimpan posisi gameplay terakhir, tetapi sebelum `PlayerList` mengirim initial world state, Lobby Systems mengganti initial level, posisi, yaw/pitch, dan gamemode ke konfigurasi Lobby. Mekanisme scheduled join teleport dari versi sebelumnya telah dihapus. `compile.bat` / `compile.sh` tetap mendeteksi JAR versi yang sama dan menawarkan Compile ulang atau Git-only. +**v1.5.6 login placement fix (dipertahankan):** player data tetap dibaca untuk menyimpan posisi gameplay terakhir, tetapi sebelum `PlayerList` mengirim initial world state, Lobby Systems mengganti initial level, posisi, dan yaw/pitch ke konfigurasi Lobby. Gamemode disinkronkan segera setelah JOIN dan dikoreksi singkat pada +1, +5, dan +20 tick. -**v1.5.0 feature release:** Portal Visual System menambahkan permukaan swirl ungu memakai block state vanilla `minecraft:nether_portal`, dengan axis X/Z yang ditentukan otomatis dari WorldEdit selection. Frame boleh memakai block apa saja. `manageDimensionGamemode` tetap default `true`, compatibility warning Dimensional Inventories tetap aktif, dan bridge WorldEdit 7.4.x tetap dipakai. +**v1.5.7 behavior update:** Lobby Systems sekarang selalu menjadi source of truth untuk gamemode per-dimension. Opsi `manageDimensionGamemode` dihapus; config lama akan dimigrasikan otomatis dengan membuang field tersebut. Command `list`, `set`, dan `remove` selalu tersedia. Compatibility warning Dimensional Inventories tetap aktif. ### Reload feedback @@ -90,7 +90,7 @@ State penting Lobby Systems memakai separator agar mudah terlihat di server cons ============================================ ``` -## Struktur konfigurasi v1.5.6 +## Struktur konfigurasi v1.5.7 ```text config/ @@ -115,7 +115,6 @@ Contoh: "lobbyYaw": 0.0, "lobbyPitch": 0.0, "minimumPlaySeconds": 300, - "manageDimensionGamemode": true, "portalCheckIntervalTicks": 5, "portalCooldownTicks": 60, "inLobbyMessage": "§eKamu sudah berada di lobby.", @@ -164,7 +163,7 @@ Trigger portal tetap berupa region Lobby Systems. Mulai v1.5.0, portal baru yang ### `config/lobby/dimension-gamemodes.json` -Minecraft menyimpan gamemode pada player, bukan pada dimension. Lobby Systems menyimpan mapping dimension → gamemode di file ini. Jika `manageDimensionGamemode=true` (default), mapping diterapkan ketika player memasuki dimension. Jika `false`, Lobby Systems tidak mengubah gamemode player dan command gamemode menjadi viewer-only. +Minecraft menyimpan gamemode pada player, bukan pada dimension. Lobby Systems menyimpan mapping dimension → gamemode di file ini dan selalu menerapkannya pada join serta perpindahan dimension. Sejak v1.5.7, Lobby Systems adalah source of truth gamemode per-dimension dan tidak memiliki mode viewer-only. Contoh: @@ -178,7 +177,7 @@ Contoh: Gamemode yang didukung: `survival`, `creative`, `adventure`, dan `spectator`. -Jika mod **Dimensional Inventories** (`dimensional-inventories`) terpasang dan `manageDimensionGamemode=true`, Lobby Systems akan menampilkan warning di console karena kedua mod dapat mengubah gamemode player saat perpindahan dimension/pool. Agar transisi konsisten, samakan gamemode dimension/pool pada kedua mod. Jika Anda ingin Dimensional Inventories menjadi pengelola utama gamemode, set `manageDimensionGamemode=false`. +Jika mod **Dimensional Inventories** (`dimensional-inventories`) terpasang, Lobby Systems menampilkan warning di console karena kedua mod dapat memiliki aturan gamemode pada transisi dimension/pool. Lobby Systems tetap authoritative dan akan menerapkan `dimension-gamemodes.json`; untuk hasil paling konsisten, samakan konfigurasi gamemode pool Dimensional Inventories atau nonaktifkan pengelolaan gamemode pada mod tersebut bila tersedia. ## Migrasi dari v1.1.x @@ -287,7 +286,7 @@ Gunakan mapping gamemode agar player otomatis mendapat gamemode yang sesuai saat /ls gamemode set minecraft:overworld survival ``` -`/ls gamemode list` menampilkan semua mapping. Saat `manageDimensionGamemode=true`, subcommand `set` dan `remove` tersedia dan autocomplete dimension hanya muncul setelah kedua subcommand tersebut. Saat `manageDimensionGamemode=false`, command tree hanya menampilkan `/ls gamemode list`. Mapping tetap disimpan di `config/lobby/dimension-gamemodes.json` dan ikut direload oleh `/ls reload`. +Command gamemode selalu tersedia: `/ls gamemode list`, `/ls gamemode set `, dan `/ls gamemode remove `. Mapping disimpan di `config/lobby/dimension-gamemodes.json`, diterapkan langsung ke player pada dimension terkait, dan ikut direload oleh `/ls reload`. ### `/ls reload` diff --git a/gradle.properties b/gradle.properties index 84279fb..a165f41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ minecraft_version=26.2 loader_version=0.19.3 loom_version=1.17-SNAPSHOT fabric_api_version=0.156.0+26.2 -mod_version=1.5.6 +mod_version=1.5.7 maven_group=id.dartokloning archives_base_name=lobby-systems diff --git a/src/main/java/id/dartokloning/lobbysystems/ConfigManager.java b/src/main/java/id/dartokloning/lobbysystems/ConfigManager.java index 73625ca..ee821cc 100644 --- a/src/main/java/id/dartokloning/lobbysystems/ConfigManager.java +++ b/src/main/java/id/dartokloning/lobbysystems/ConfigManager.java @@ -2,6 +2,8 @@ package id.dartokloning.lobbysystems; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import java.io.Reader; import java.io.Writer; @@ -24,10 +26,16 @@ public final class ConfigManager { } try (Reader reader = Files.newBufferedReader(PATH)) { - LobbyConfig loaded = GSON.fromJson(reader, LobbyConfig.class); + JsonObject root = JsonParser.parseReader(reader).getAsJsonObject(); + boolean hadDeprecatedGamemodeFlag = root.has("manageDimensionGamemode"); + LobbyConfig loaded = GSON.fromJson(root, LobbyConfig.class); if (loaded == null) throw new IllegalArgumentException("Config JSON kosong atau tidak valid."); validate(loaded); config = loaded; + if (hadDeprecatedGamemodeFlag) { + LobbySystemsMod.LOGGER.info("Removing deprecated config option manageDimensionGamemode; Lobby Systems now always manages dimension gamemodes."); + save(); + } return true; } catch (Exception e) { LobbySystemsMod.LOGGER.error("Failed to load {}. Keeping previous config.", PATH, e); diff --git a/src/main/java/id/dartokloning/lobbysystems/LobbyConfig.java b/src/main/java/id/dartokloning/lobbysystems/LobbyConfig.java index 0824f27..7825874 100644 --- a/src/main/java/id/dartokloning/lobbysystems/LobbyConfig.java +++ b/src/main/java/id/dartokloning/lobbysystems/LobbyConfig.java @@ -9,8 +9,6 @@ public final class LobbyConfig { public float lobbyPitch = 0.0f; public int minimumPlaySeconds = 300; - /** Jika true, Lobby Systems menjadi source of truth gamemode per dimension. */ - public boolean manageDimensionGamemode = true; /** Seberapa sering region portal diperiksa. 5 tick = 4 kali/detik. */ public int portalCheckIntervalTicks = 5; diff --git a/src/main/java/id/dartokloning/lobbysystems/LobbySystemsMod.java b/src/main/java/id/dartokloning/lobbysystems/LobbySystemsMod.java index 06cd470..7e486ef 100644 --- a/src/main/java/id/dartokloning/lobbysystems/LobbySystemsMod.java +++ b/src/main/java/id/dartokloning/lobbysystems/LobbySystemsMod.java @@ -137,10 +137,8 @@ public final class LobbySystemsMod implements ModInitializer { // sebelum paket login/world awal dikirim. Di titik JOIN ini kita hanya // sinkronkan state runtime dan memberi enforcement singkat sebagai proteksi // terhadap mod lain yang mengubah gamemode tepat setelah login. - if (CONFIG.get().manageDimensionGamemode) { - DIMENSION_GAMEMODES.applyForPlayer(player, true); - DIMENSION_GAMEMODES.beginJoinEnforcement(player.getUUID()); - } + DIMENSION_GAMEMODES.applyForPlayer(player, true); + DIMENSION_GAMEMODES.beginJoinEnforcement(player.getUUID()); SESSIONS.onJoin(player.getUUID(), lobbyKey()); LobbyLog.state( @@ -170,9 +168,7 @@ public final class LobbySystemsMod implements ModInitializer { ServerTickEvents.END_SERVER_TICK.register(server -> { TELEPORTS.tick(); - if (CONFIG.get().manageDimensionGamemode) { - DIMENSION_GAMEMODES.tick(server); - } + DIMENSION_GAMEMODES.tick(server); ResourceKey lobby = lobbyKey(); for (ServerPlayer player : server.getPlayerList().getPlayers()) { @@ -257,7 +253,6 @@ public final class LobbySystemsMod implements ModInitializer { .then(Commands.literal("list") .executes(ctx -> listDimensionGameModes(ctx.getSource()))) .then(Commands.literal("set") - .requires(source -> CONFIG.get().manageDimensionGamemode) .then(Commands.argument("dimension", IdentifierArgument.id()) .suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource( ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder)) @@ -270,7 +265,6 @@ public final class LobbySystemsMod implements ModInitializer { IdentifierArgument.getId(ctx, "dimension").toString(), StringArgumentType.getString(ctx, "mode")))))) .then(Commands.literal("remove") - .requires(source -> CONFIG.get().manageDimensionGamemode) .then(Commands.argument("dimension", IdentifierArgument.id()) .suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource( ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder)) @@ -382,10 +376,8 @@ public final class LobbySystemsMod implements ModInitializer { LobbyLog.state("RELOAD FAILED", "Reason: dimension-gamemodes.json gagal dibaca"); return 0; } - if (CONFIG.get().manageDimensionGamemode) { - for (String dimension : DIMENSION_GAMEMODES.all().keySet()) { - DIMENSION_GAMEMODES.applyToDimension(server, dimension); - } + for (String dimension : DIMENSION_GAMEMODES.all().keySet()) { + DIMENSION_GAMEMODES.applyToDimension(server, dimension); } logDimensionInventoryCompatibility(); refreshCommandTrees(server); @@ -410,8 +402,7 @@ public final class LobbySystemsMod implements ModInitializer { "Minimum play: " + CONFIG.get().minimumPlaySeconds + "s", "Portals: " + PORTALS.size(), "Portal visual blocks restored: " + repairedPortalVisuals, - "Dimension gamemodes: " + DIMENSION_GAMEMODES.all().size() - + " (management " + (CONFIG.get().manageDimensionGamemode ? "enabled" : "disabled") + ")", + "Dimension gamemodes: " + DIMENSION_GAMEMODES.all().size() + " (authoritative)", "Dimension gamerules: " + DIMENSION_GAMERULES.all().size() + " dimensions / " + gameRuleSummary.applied() + " rules applied", "Player positions lobby exclusion: active" ); @@ -604,26 +595,19 @@ public final class LobbySystemsMod implements ModInitializer { private static int listDimensionGameModes(CommandSourceStack source) { Map all = DIMENSION_GAMEMODES.all(); if (all.isEmpty()) { - String hint = CONFIG.get().manageDimensionGamemode - ? "\n§7Gunakan §f/ls gamemode set " - : "\n§7Management gamemode sedang §cdisabled§7; mode viewer-only aktif."; sendCommandMessage(source, Component.literal( - "§eBelum ada gamemode dimension yang dikonfigurasi." + hint)); + "§eBelum ada gamemode dimension yang dikonfigurasi." + + "\n§7Gunakan §f/ls gamemode set ")); return 1; } sendCommandMessage(source, Component.literal( - "§6§lLobby Systems Dimension Gamemodes §r§7(" + all.size() + ")" - + (CONFIG.get().manageDimensionGamemode ? " §8[managed]" : " §8[viewer-only]"))); + "§6§lLobby Systems Dimension Gamemodes §r§7(" + all.size() + ") §8[authoritative]")); all.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> sendCommandMessage(source, Component.literal("§8• §f" + entry.getKey() + " §7= §a" + entry.getValue()))); return all.size(); } private static int setDimensionGameMode(CommandSourceStack source, MinecraftServer server, String dimensionId, String rawMode) { - if (!CONFIG.get().manageDimensionGamemode) { - source.sendFailure(Component.literal("§cDimension gamemode management sedang disabled di config.json.")); - return 0; - } ServerLevel level = findLevel(server, dimensionId); if (level == null) { source.sendFailure(Component.literal("§cDimension '" + dimensionId + "' tidak ditemukan atau tidak ter-load.")); @@ -653,10 +637,6 @@ public final class LobbySystemsMod implements ModInitializer { } private static int removeDimensionGameMode(CommandSourceStack source, String dimensionId) { - if (!CONFIG.get().manageDimensionGamemode) { - source.sendFailure(Component.literal("§cDimension gamemode management sedang disabled di config.json.")); - return 0; - } if (!DIMENSION_GAMEMODES.remove(dimensionId)) { source.sendFailure(Component.literal("§cTidak ada mapping gamemode untuk dimension '" + dimensionId + "'.")); return 0; @@ -673,22 +653,13 @@ public final class LobbySystemsMod implements ModInitializer { return; } - if (CONFIG.get().manageDimensionGamemode) { - LobbyLog.state( - "COMPATIBILITY WARNING", - "Dimensional Inventories: detected", - "manageDimensionGamemode: true", - "Both mods can apply player gamemode on dimension/pool changes.", - "Keep matching gamemodes in Lobby Systems and Dimensional Inventories to avoid conflicting transitions." - ); - } else { - LobbyLog.state( - "DIMENSION GAMEMODE COMPATIBILITY", - "Dimensional Inventories: detected", - "manageDimensionGamemode: false", - "Lobby Systems gamemode commands are viewer-only; Dimensional Inventories may manage player gamemode." - ); - } + LobbyLog.state( + "COMPATIBILITY WARNING", + "Dimensional Inventories: detected", + "Lobby Systems dimension gamemode management: authoritative / always enabled", + "Lobby Systems will enforce dimension-gamemodes.json on join and dimension changes.", + "Keep Dimensional Inventories pool gamemodes aligned (or disable its gamemode handling) to avoid conflicting transitions." + ); } private static void refreshCommandTrees(MinecraftServer server) { @@ -926,9 +897,7 @@ public final class LobbySystemsMod implements ModInitializer { boolean ok = TELEPORTS.teleport(player, target, x, y, z, yaw, pitch); if (!ok) return 0; - if (CONFIG.get().manageDimensionGamemode) { - DIMENSION_GAMEMODES.applyForPlayer(player, true); - } + DIMENSION_GAMEMODES.applyForPlayer(player, true); if (sendMessage) { player.sendSystemMessage(Component.literal(returning @@ -988,9 +957,7 @@ public final class LobbySystemsMod implements ModInitializer { "Position: [" + String.format("%.2f", c.lobbyX) + ", " + String.format("%.2f", c.lobbyY) + ", " + String.format("%.2f", c.lobbyZ) + "]", - "Gamemode: " + (c.manageDimensionGamemode - ? String.valueOf(DIMENSION_GAMEMODES.get(c.lobbyDimension)) - : "unmanaged") + "Gamemode: " + String.valueOf(DIMENSION_GAMEMODES.get(c.lobbyDimension)) ); } @@ -1032,7 +999,7 @@ public final class LobbySystemsMod implements ModInitializer { LobbyConfig c = CONFIG.get(); boolean ok = TELEPORTS.teleport(player, target, c.lobbyX, c.lobbyY, c.lobbyZ, c.lobbyYaw, c.lobbyPitch); - if (ok && CONFIG.get().manageDimensionGamemode) { + if (ok) { DIMENSION_GAMEMODES.applyForPlayer(player, true); } if (ok && sendMessage) player.sendSystemMessage(Component.literal(c.returnedMessage));