Bugfix compatibility
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
# Lobby Systems v1.4.0 — Fabric 26.2
|
# Lobby Systems v1.4.1 — Fabric 26.2
|
||||||
|
|
||||||
Server-side lobby routing untuk Minecraft 26.2/Fabric. v1.4.0 menambahkan **native persistent per-dimension gamerules** milik Lobby Systems, tetap mempertahankan portal antar-dimensi, last-position, minimum playtime, gamerule viewer ber-pagination, serta per-dimension player gamemode.
|
Server-side lobby routing untuk Minecraft 26.2/Fabric. v1.4.1 mempertahankan native persistent per-dimension gamerules dan menambahkan compatibility control untuk dimension gamemode serta perbaikan WorldEdit 7.4.x portal selection.
|
||||||
|
|
||||||
**v1.4.0 feature release:** gamerule tidak lagi bergantung pada WorldGameRules. Lobby Systems membuat instance `GameRules` terpisah untuk setiap `ServerLevel`, menyimpan override ke `config/lobby/dimension-gamerules.json`, lalu menerapkannya kembali saat world load, server start, dan `/ls reload`. WorldGameRules harus dihapus karena kedua mod mengubah mekanisme gamerule per-world.
|
**v1.4.1 compatibility/bugfix release:** `manageDimensionGamemode` (default `true`) menentukan apakah Lobby Systems menjadi source of truth gamemode per dimension. Jika Dimensional Inventories terdeteksi saat opsi ini aktif, console menampilkan compatibility warning agar konfigurasi gamemode kedua mod disamakan. Integrasi WorldEdit juga diperbarui untuk API Fabric WorldEdit 7.4.x (`FabricAdapter.get().fromNativePlayer(...)`).
|
||||||
|
|
||||||
### Reload feedback
|
### Reload feedback
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ chmod +x compile.sh release.sh
|
|||||||
```text
|
```text
|
||||||
============================================
|
============================================
|
||||||
Lobby Systems - Fabric 26.2 Build
|
Lobby Systems - Fabric 26.2 Build
|
||||||
Version: 1.4.0
|
Version: 1.4.1
|
||||||
Namespace: id.dartokloning.lobbysystems
|
Namespace: id.dartokloning.lobbysystems
|
||||||
Mod ID: dartokloning
|
Mod ID: dartokloning
|
||||||
============================================
|
============================================
|
||||||
@@ -49,7 +49,7 @@ chmod +x compile.sh release.sh
|
|||||||
Output utama:
|
Output utama:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
build/libs/lobby-systems-1.4.0.jar
|
build/libs/lobby-systems-1.4.1.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
`*-sources.jar` hanya source archive dan tidak perlu dipasang ke server.
|
`*-sources.jar` hanya source archive dan tidak perlu dipasang ke server.
|
||||||
@@ -80,14 +80,14 @@ State penting Lobby Systems memakai separator agar mudah terlihat di server cons
|
|||||||
```text
|
```text
|
||||||
============================================
|
============================================
|
||||||
Lobby Systems - READY
|
Lobby Systems - READY
|
||||||
Version: 1.4.0
|
Version: 1.4.1
|
||||||
Minecraft: 26.2
|
Minecraft: 26.2
|
||||||
Config directory: config/lobby
|
Config directory: config/lobby
|
||||||
WorldEdit: available
|
WorldEdit: available
|
||||||
============================================
|
============================================
|
||||||
```
|
```
|
||||||
|
|
||||||
## Struktur konfigurasi v1.4.0
|
## Struktur konfigurasi v1.4.1
|
||||||
|
|
||||||
```text
|
```text
|
||||||
config/
|
config/
|
||||||
@@ -112,6 +112,7 @@ Contoh:
|
|||||||
"lobbyYaw": 0.0,
|
"lobbyYaw": 0.0,
|
||||||
"lobbyPitch": 0.0,
|
"lobbyPitch": 0.0,
|
||||||
"minimumPlaySeconds": 300,
|
"minimumPlaySeconds": 300,
|
||||||
|
"manageDimensionGamemode": true,
|
||||||
"portalCheckIntervalTicks": 5,
|
"portalCheckIntervalTicks": 5,
|
||||||
"portalCooldownTicks": 60,
|
"portalCooldownTicks": 60,
|
||||||
"inLobbyMessage": "§eKamu sudah berada di lobby.",
|
"inLobbyMessage": "§eKamu sudah berada di lobby.",
|
||||||
@@ -158,7 +159,7 @@ Portal adalah region/cuboid, bukan harus Nether Portal asli. Anda bebas membuat
|
|||||||
|
|
||||||
### `config/lobby/dimension-gamemodes.json`
|
### `config/lobby/dimension-gamemodes.json`
|
||||||
|
|
||||||
Minecraft menyimpan gamemode pada player, bukan pada dimension. Lobby Systems v1.4.0 menambahkan mapping dimension → gamemode dan menerapkannya ketika player masuk ke dimension tersebut. File ini dibuat otomatis dan dapat dikelola lewat command admin.
|
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.
|
||||||
|
|
||||||
Contoh:
|
Contoh:
|
||||||
|
|
||||||
@@ -172,6 +173,8 @@ Contoh:
|
|||||||
|
|
||||||
Gamemode yang didukung: `survival`, `creative`, `adventure`, dan `spectator`.
|
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`.
|
||||||
|
|
||||||
## Migrasi dari v1.1.x
|
## Migrasi dari v1.1.x
|
||||||
|
|
||||||
Pada startup pertama v1.4.0:
|
Pada startup pertama v1.4.0:
|
||||||
@@ -224,7 +227,6 @@ Jika file tujuan baru sudah ada, migrator tidak menimpanya.
|
|||||||
/ls gamerule set <dimension> <rule> <value>
|
/ls gamerule set <dimension> <rule> <value>
|
||||||
/ls gamerule reset <dimension> <rule>
|
/ls gamerule reset <dimension> <rule>
|
||||||
|
|
||||||
/ls gamemode <dimension>
|
|
||||||
/ls gamemode list
|
/ls gamemode list
|
||||||
/ls gamemode set <dimension> <survival|creative|adventure|spectator>
|
/ls gamemode set <dimension> <survival|creative|adventure|spectator>
|
||||||
/ls gamemode remove <dimension>
|
/ls gamemode remove <dimension>
|
||||||
@@ -280,7 +282,7 @@ Gunakan mapping gamemode agar player otomatis mendapat gamemode yang sesuai saat
|
|||||||
/ls gamemode set minecraft:overworld survival
|
/ls gamemode set minecraft:overworld survival
|
||||||
```
|
```
|
||||||
|
|
||||||
`/ls gamemode <dimension>` melihat mapping satu dimension, `/ls gamemode list` melihat semua mapping, dan `/ls gamemode remove <dimension>` menghapus mapping. Mapping disimpan ke `config/lobby/dimension-gamemodes.json` dan ikut direload oleh `/ls reload`.
|
`/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`.
|
||||||
|
|
||||||
### `/ls reload`
|
### `/ls reload`
|
||||||
|
|
||||||
@@ -322,7 +324,7 @@ Contoh lain:
|
|||||||
|
|
||||||
Nama portal hanya menerima huruf kecil `a-z`, angka `0-9`, `_`, dan `-`.
|
Nama portal hanya menerima huruf kecil `a-z`, angka `0-9`, `_`, dan `-`.
|
||||||
|
|
||||||
WorldEdit hanya dibutuhkan saat membaca selection untuk **membuat** portal. Setelah `portals.json` tersimpan, portal runtime dikelola Lobby Systems sendiri. Jika WorldEdit dilepas kemudian, portal yang sudah ada tetap dapat bekerja.
|
WorldEdit hanya dibutuhkan saat membaca selection untuk **membuat** portal. v1.4.1 memakai bridge WorldEdit Fabric 7.4.x melalui `FabricAdapter.get().fromNativePlayer(...)` dengan fallback API lama. Setelah `portals.json` tersimpan, portal runtime dikelola Lobby Systems sendiri. Jika WorldEdit dilepas kemudian, portal yang sudah ada tetap dapat bekerja.
|
||||||
|
|
||||||
### Menghapus dan mengecek portal
|
### Menghapus dan mengecek portal
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,6 +5,6 @@ minecraft_version=26.2
|
|||||||
loader_version=0.19.3
|
loader_version=0.19.3
|
||||||
loom_version=1.17-SNAPSHOT
|
loom_version=1.17-SNAPSHOT
|
||||||
fabric_api_version=0.156.0+26.2
|
fabric_api_version=0.156.0+26.2
|
||||||
mod_version=1.4.0
|
mod_version=1.4.1
|
||||||
maven_group=id.dartokloning
|
maven_group=id.dartokloning
|
||||||
archives_base_name=lobby-systems
|
archives_base_name=lobby-systems
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ public final class LobbyConfig {
|
|||||||
public float lobbyPitch = 0.0f;
|
public float lobbyPitch = 0.0f;
|
||||||
public int minimumPlaySeconds = 300;
|
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. */
|
/** Seberapa sering region portal diperiksa. 5 tick = 4 kali/detik. */
|
||||||
public int portalCheckIntervalTicks = 5;
|
public int portalCheckIntervalTicks = 5;
|
||||||
/** Cooldown setelah portal terpicu. 60 tick = 3 detik. */
|
/** Cooldown setelah portal terpicu. 60 tick = 3 detik. */
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package id.dartokloning.lobbysystems;
|
|||||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||||
@@ -79,6 +80,8 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
"Configured dimensions: " + DIMENSION_GAMEMODES.all().size()
|
"Configured dimensions: " + DIMENSION_GAMEMODES.all().size()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logDimensionInventoryCompatibility();
|
||||||
|
|
||||||
boolean gameRulesLoaded = DIMENSION_GAMERULES.load();
|
boolean gameRulesLoaded = DIMENSION_GAMERULES.load();
|
||||||
LobbyLog.state(
|
LobbyLog.state(
|
||||||
gameRulesLoaded ? "DIMENSION GAMERULES LOADED" : "DIMENSION GAMERULES LOAD FAILED",
|
gameRulesLoaded ? "DIMENSION GAMERULES LOADED" : "DIMENSION GAMERULES LOAD FAILED",
|
||||||
@@ -145,7 +148,9 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (CONFIG.get().manageDimensionGamemode) {
|
||||||
DIMENSION_GAMEMODES.tick(server);
|
DIMENSION_GAMEMODES.tick(server);
|
||||||
|
}
|
||||||
|
|
||||||
ResourceKey<Level> lobby = lobbyKey();
|
ResourceKey<Level> lobby = lobbyKey();
|
||||||
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
||||||
@@ -229,6 +234,7 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
.then(Commands.literal("list")
|
.then(Commands.literal("list")
|
||||||
.executes(ctx -> listDimensionGameModes(ctx.getSource())))
|
.executes(ctx -> listDimensionGameModes(ctx.getSource())))
|
||||||
.then(Commands.literal("set")
|
.then(Commands.literal("set")
|
||||||
|
.requires(source -> CONFIG.get().manageDimensionGamemode)
|
||||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
.then(Commands.argument("dimension", IdentifierArgument.id())
|
||||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
||||||
@@ -241,18 +247,13 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
IdentifierArgument.getId(ctx, "dimension").toString(),
|
IdentifierArgument.getId(ctx, "dimension").toString(),
|
||||||
StringArgumentType.getString(ctx, "mode"))))))
|
StringArgumentType.getString(ctx, "mode"))))))
|
||||||
.then(Commands.literal("remove")
|
.then(Commands.literal("remove")
|
||||||
|
.requires(source -> CONFIG.get().manageDimensionGamemode)
|
||||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
.then(Commands.argument("dimension", IdentifierArgument.id())
|
||||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
||||||
.executes(ctx -> removeDimensionGameMode(
|
.executes(ctx -> removeDimensionGameMode(
|
||||||
ctx.getSource(),
|
ctx.getSource(),
|
||||||
IdentifierArgument.getId(ctx, "dimension").toString()))))
|
IdentifierArgument.getId(ctx, "dimension").toString())))))
|
||||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
|
||||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
|
||||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
|
||||||
.executes(ctx -> showDimensionGameMode(
|
|
||||||
ctx.getSource(),
|
|
||||||
IdentifierArgument.getId(ctx, "dimension").toString()))))
|
|
||||||
.then(Commands.literal("portal")
|
.then(Commands.literal("portal")
|
||||||
.then(Commands.literal("create")
|
.then(Commands.literal("create")
|
||||||
.then(Commands.argument("name", StringArgumentType.word())
|
.then(Commands.argument("name", StringArgumentType.word())
|
||||||
@@ -357,9 +358,13 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
LobbyLog.state("RELOAD FAILED", "Reason: dimension-gamemodes.json gagal dibaca");
|
LobbyLog.state("RELOAD FAILED", "Reason: dimension-gamemodes.json gagal dibaca");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (CONFIG.get().manageDimensionGamemode) {
|
||||||
for (String dimension : DIMENSION_GAMEMODES.all().keySet()) {
|
for (String dimension : DIMENSION_GAMEMODES.all().keySet()) {
|
||||||
DIMENSION_GAMEMODES.applyToDimension(server, dimension);
|
DIMENSION_GAMEMODES.applyToDimension(server, dimension);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
logDimensionInventoryCompatibility();
|
||||||
|
refreshCommandTrees(server);
|
||||||
|
|
||||||
boolean gameRulesOk = DIMENSION_GAMERULES.load();
|
boolean gameRulesOk = DIMENSION_GAMERULES.load();
|
||||||
if (!gameRulesOk) {
|
if (!gameRulesOk) {
|
||||||
@@ -380,7 +385,8 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
"Lobby: " + CONFIG.get().lobbyDimension,
|
"Lobby: " + CONFIG.get().lobbyDimension,
|
||||||
"Minimum play: " + CONFIG.get().minimumPlaySeconds + "s",
|
"Minimum play: " + CONFIG.get().minimumPlaySeconds + "s",
|
||||||
"Portals: " + PORTALS.size(),
|
"Portals: " + PORTALS.size(),
|
||||||
"Dimension gamemodes: " + DIMENSION_GAMEMODES.all().size(),
|
"Dimension gamemodes: " + DIMENSION_GAMEMODES.all().size()
|
||||||
|
+ " (management " + (CONFIG.get().manageDimensionGamemode ? "enabled" : "disabled") + ")",
|
||||||
"Dimension gamerules: " + DIMENSION_GAMERULES.all().size() + " dimensions / " + gameRuleSummary.applied() + " rules applied",
|
"Dimension gamerules: " + DIMENSION_GAMERULES.all().size() + " dimensions / " + gameRuleSummary.applied() + " rules applied",
|
||||||
"Player positions lobby exclusion: active"
|
"Player positions lobby exclusion: active"
|
||||||
);
|
);
|
||||||
@@ -570,34 +576,29 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
return rule.serialize(level.getGameRules().get(rule));
|
return rule.serialize(level.getGameRules().get(rule));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int showDimensionGameMode(CommandSourceStack source, String dimensionId) {
|
|
||||||
String mode = DIMENSION_GAMEMODES.get(dimensionId);
|
|
||||||
if (mode == null) {
|
|
||||||
sendCommandMessage(source, Component.literal(
|
|
||||||
"§6Lobby Systems Gamemode\n§7Dimension: §f" + dimensionId
|
|
||||||
+ "\n§7Configured: §e<none>"
|
|
||||||
+ "\n§8Player akan mempertahankan gamemode sebelumnya sampai mapping dibuat."));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
sendCommandMessage(source, Component.literal(
|
|
||||||
"§6Lobby Systems Gamemode\n§7Dimension: §f" + dimensionId + "\n§7Gamemode: §a" + mode));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int listDimensionGameModes(CommandSourceStack source) {
|
private static int listDimensionGameModes(CommandSourceStack source) {
|
||||||
Map<String, String> all = DIMENSION_GAMEMODES.all();
|
Map<String, String> all = DIMENSION_GAMEMODES.all();
|
||||||
if (all.isEmpty()) {
|
if (all.isEmpty()) {
|
||||||
|
String hint = CONFIG.get().manageDimensionGamemode
|
||||||
|
? "\n§7Gunakan §f/ls gamemode set <dimension> <mode>"
|
||||||
|
: "\n§7Management gamemode sedang §cdisabled§7; mode viewer-only aktif.";
|
||||||
sendCommandMessage(source, Component.literal(
|
sendCommandMessage(source, Component.literal(
|
||||||
"§eBelum ada gamemode dimension yang dikonfigurasi.\n§7Gunakan §f/ls gamemode set <dimension> <mode>"));
|
"§eBelum ada gamemode dimension yang dikonfigurasi." + hint));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
sendCommandMessage(source, Component.literal("§6§lLobby Systems Dimension Gamemodes §r§7(" + all.size() + ")"));
|
sendCommandMessage(source, Component.literal(
|
||||||
|
"§6§lLobby Systems Dimension Gamemodes §r§7(" + all.size() + ")"
|
||||||
|
+ (CONFIG.get().manageDimensionGamemode ? " §8[managed]" : " §8[viewer-only]")));
|
||||||
all.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry ->
|
all.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry ->
|
||||||
sendCommandMessage(source, Component.literal("§8• §f" + entry.getKey() + " §7= §a" + entry.getValue())));
|
sendCommandMessage(source, Component.literal("§8• §f" + entry.getKey() + " §7= §a" + entry.getValue())));
|
||||||
return all.size();
|
return all.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int setDimensionGameMode(CommandSourceStack source, MinecraftServer server, String dimensionId, String rawMode) {
|
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);
|
ServerLevel level = findLevel(server, dimensionId);
|
||||||
if (level == null) {
|
if (level == null) {
|
||||||
source.sendFailure(Component.literal("§cDimension '" + dimensionId + "' tidak ditemukan atau tidak ter-load."));
|
source.sendFailure(Component.literal("§cDimension '" + dimensionId + "' tidak ditemukan atau tidak ter-load."));
|
||||||
@@ -627,6 +628,10 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int removeDimensionGameMode(CommandSourceStack source, String dimensionId) {
|
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)) {
|
if (!DIMENSION_GAMEMODES.remove(dimensionId)) {
|
||||||
source.sendFailure(Component.literal("§cTidak ada mapping gamemode untuk dimension '" + dimensionId + "'."));
|
source.sendFailure(Component.literal("§cTidak ada mapping gamemode untuk dimension '" + dimensionId + "'."));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -638,6 +643,39 @@ public final class LobbySystemsMod implements ModInitializer {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void logDimensionInventoryCompatibility() {
|
||||||
|
if (!FabricLoader.getInstance().isModLoaded("dimensional-inventories")) {
|
||||||
|
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."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void refreshCommandTrees(MinecraftServer server) {
|
||||||
|
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
||||||
|
try {
|
||||||
|
server.getCommands().sendCommands(player);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
LOGGER.warn("Failed to refresh command tree for {} after config reload.", player.getGameProfile().name(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private record GameRuleEntry(String name, String value) {}
|
private record GameRuleEntry(String name, String value) {}
|
||||||
|
|
||||||
private static int createPortal(ServerPlayer player, MinecraftServer server, String rawName, String rawTarget) {
|
private static int createPortal(ServerPlayer player, MinecraftServer server, String rawName, String rawTarget) {
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import java.lang.reflect.Modifier;
|
|||||||
/**
|
/**
|
||||||
* Soft integration dengan WorldEdit. Reflection sengaja dipakai agar Lobby Systems
|
* Soft integration dengan WorldEdit. Reflection sengaja dipakai agar Lobby Systems
|
||||||
* tetap dapat dijalankan tanpa WorldEdit; portal yang sudah tersimpan tetap bekerja.
|
* tetap dapat dijalankan tanpa WorldEdit; portal yang sudah tersimpan tetap bekerja.
|
||||||
|
*
|
||||||
|
* Mendukung WorldEdit Fabric 7.4.x (FabricAdapter.get().fromNativePlayer(...))
|
||||||
|
* sekaligus fallback API lama agar bridge tidak rapuh saat server memakai build berbeda.
|
||||||
*/
|
*/
|
||||||
public final class WorldEditSelectionBridge {
|
public final class WorldEditSelectionBridge {
|
||||||
private WorldEditSelectionBridge() {}
|
private WorldEditSelectionBridge() {}
|
||||||
@@ -23,13 +26,10 @@ public final class WorldEditSelectionBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Class<?> adapterClass = Class.forName("com.sk89q.worldedit.fabric.FabricAdapter");
|
Class<?> adapterClass = Class.forName("com.sk89q.worldedit.fabric.FabricAdapter");
|
||||||
Object actor;
|
Object actor = adaptPlayer(adapterClass, player);
|
||||||
try {
|
if (actor == null) {
|
||||||
actor = invokeCompatibleStatic(adapterClass, "adaptPlayer", player);
|
throw new IllegalStateException("FabricAdapter tidak dapat mengadaptasi player.");
|
||||||
} catch (NoSuchMethodException ignored) {
|
|
||||||
actor = invokeCompatibleStatic(adapterClass, "adapt", player);
|
|
||||||
}
|
}
|
||||||
if (actor == null) throw new IllegalStateException("FabricAdapter tidak dapat mengadaptasi player.");
|
|
||||||
|
|
||||||
Class<?> worldEditClass = Class.forName("com.sk89q.worldedit.WorldEdit");
|
Class<?> worldEditClass = Class.forName("com.sk89q.worldedit.WorldEdit");
|
||||||
Object worldEdit = worldEditClass.getMethod("getInstance").invoke(null);
|
Object worldEdit = worldEditClass.getMethod("getInstance").invoke(null);
|
||||||
@@ -53,6 +53,30 @@ public final class WorldEditSelectionBridge {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object adaptPlayer(Class<?> adapterClass, ServerPlayer player) throws Exception {
|
||||||
|
// WorldEdit 7.4.x Fabric API: FabricAdapter.get().fromNativePlayer(ServerPlayer)
|
||||||
|
try {
|
||||||
|
Object adapter = adapterClass.getMethod("get").invoke(null);
|
||||||
|
try {
|
||||||
|
return invokeCompatible(adapter, "fromNativePlayer", player);
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
// Some CoreMcAdapter builds may expose a differently named instance bridge.
|
||||||
|
try {
|
||||||
|
return invokeCompatible(adapter, "adaptPlayer", player);
|
||||||
|
} catch (NoSuchMethodException ignoredAgain) {
|
||||||
|
return invokeCompatible(adapter, "adapt", player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
// Backward compatibility for older WorldEdit Fabric adapters.
|
||||||
|
try {
|
||||||
|
return invokeCompatibleStatic(adapterClass, "adaptPlayer", player);
|
||||||
|
} catch (NoSuchMethodException ignoredAgain) {
|
||||||
|
return invokeCompatibleStatic(adapterClass, "adapt", player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int intValue(Object object, String method) throws Exception {
|
private static int intValue(Object object, String method) throws Exception {
|
||||||
return ((Number) object.getClass().getMethod(method).invoke(object)).intValue();
|
return ((Number) object.getClass().getMethod(method).invoke(object)).intValue();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user