Add gamerule per dimension
This commit is contained in:
@@ -10,6 +10,7 @@ public final class ConfigPaths {
|
||||
public static final Path PLAYER_POSITIONS = DIRECTORY.resolve("player-positions.json");
|
||||
public static final Path PORTALS = DIRECTORY.resolve("portals.json");
|
||||
public static final Path DIMENSION_GAMEMODES = DIRECTORY.resolve("dimension-gamemodes.json");
|
||||
public static final Path DIMENSION_GAMERULES = DIRECTORY.resolve("dimension-gamerules.json");
|
||||
|
||||
private ConfigPaths() {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package id.dartokloning.lobbysystems;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.gamerules.GameRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Persistent per-dimension gamerule overrides owned by Lobby Systems.
|
||||
*
|
||||
* Values are stored in config/lobby/dimension-gamerules.json and are applied
|
||||
* directly to each ServerLevel's independent GameRules instance.
|
||||
*/
|
||||
public final class DimensionGameRuleManager {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private final Map<String, Map<String, String>> configured = new LinkedHashMap<>();
|
||||
|
||||
public boolean load() {
|
||||
configured.clear();
|
||||
try {
|
||||
Files.createDirectories(ConfigPaths.DIRECTORY);
|
||||
if (!Files.exists(ConfigPaths.DIMENSION_GAMERULES)) {
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
String json = Files.readString(ConfigPaths.DIMENSION_GAMERULES, StandardCharsets.UTF_8);
|
||||
if (json.isBlank()) {
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
JsonElement rootElement = JsonParser.parseString(json);
|
||||
if (!rootElement.isJsonObject()) {
|
||||
LobbySystemsMod.LOGGER.error("dimension-gamerules.json root must be a JSON object");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, JsonElement> dimensionEntry : rootElement.getAsJsonObject().entrySet()) {
|
||||
if (!dimensionEntry.getValue().isJsonObject()) {
|
||||
LobbySystemsMod.LOGGER.warn("Ignoring gamerule config for '{}': expected an object", dimensionEntry.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, String> rules = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, JsonElement> ruleEntry : dimensionEntry.getValue().getAsJsonObject().entrySet()) {
|
||||
JsonElement value = ruleEntry.getValue();
|
||||
if (!value.isJsonPrimitive()) {
|
||||
LobbySystemsMod.LOGGER.warn("Ignoring gamerule '{} / {}': value must be boolean, number, or string",
|
||||
dimensionEntry.getKey(), ruleEntry.getKey());
|
||||
continue;
|
||||
}
|
||||
rules.put(ruleEntry.getKey(), value.getAsString());
|
||||
}
|
||||
configured.put(dimensionEntry.getKey(), rules);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LobbySystemsMod.LOGGER.error("Failed to load {}", ConfigPaths.DIMENSION_GAMERULES, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
try {
|
||||
Files.createDirectories(ConfigPaths.DIRECTORY);
|
||||
JsonObject root = new JsonObject();
|
||||
configured.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(dimensionEntry -> {
|
||||
JsonObject rules = new JsonObject();
|
||||
dimensionEntry.getValue().entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(ruleEntry -> {
|
||||
String value = ruleEntry.getValue();
|
||||
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
|
||||
rules.addProperty(ruleEntry.getKey(), Boolean.parseBoolean(value));
|
||||
} else {
|
||||
try {
|
||||
rules.addProperty(ruleEntry.getKey(), Integer.parseInt(value));
|
||||
} catch (NumberFormatException ignored) {
|
||||
rules.addProperty(ruleEntry.getKey(), value);
|
||||
}
|
||||
}
|
||||
});
|
||||
root.add(dimensionEntry.getKey(), rules);
|
||||
});
|
||||
Files.writeString(ConfigPaths.DIMENSION_GAMERULES, GSON.toJson(root) + System.lineSeparator(), StandardCharsets.UTF_8);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LobbySystemsMod.LOGGER.error("Failed to save {}", ConfigPaths.DIMENSION_GAMERULES, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Map<String, String>> all() {
|
||||
Map<String, Map<String, String>> copy = new LinkedHashMap<>();
|
||||
configured.forEach((dimension, rules) -> copy.put(dimension, Map.copyOf(rules)));
|
||||
return Map.copyOf(copy);
|
||||
}
|
||||
|
||||
public boolean set(String dimensionId, String ruleId, String serializedValue) {
|
||||
configured.computeIfAbsent(dimensionId, ignored -> new LinkedHashMap<>()).put(ruleId, serializedValue);
|
||||
return save();
|
||||
}
|
||||
|
||||
public boolean remove(String dimensionId, String ruleId) {
|
||||
Map<String, String> rules = configured.get(dimensionId);
|
||||
if (rules == null || rules.remove(ruleId) == null) {
|
||||
return false;
|
||||
}
|
||||
if (rules.isEmpty()) {
|
||||
configured.remove(dimensionId);
|
||||
}
|
||||
return save();
|
||||
}
|
||||
|
||||
public ApplySummary applyAll(MinecraftServer server) {
|
||||
int dimensions = 0;
|
||||
int applied = 0;
|
||||
int skipped = 0;
|
||||
for (Map.Entry<String, Map<String, String>> dimensionEntry : configured.entrySet()) {
|
||||
ServerLevel level = LobbySystemsMod.findLevelForManager(server, dimensionEntry.getKey());
|
||||
if (level == null) {
|
||||
skipped += dimensionEntry.getValue().size();
|
||||
continue;
|
||||
}
|
||||
dimensions++;
|
||||
ApplySummary summary = applyToLevel(level);
|
||||
applied += summary.applied();
|
||||
skipped += summary.skipped();
|
||||
}
|
||||
return new ApplySummary(dimensions, applied, skipped);
|
||||
}
|
||||
|
||||
public ApplySummary applyToLevel(ServerLevel level) {
|
||||
Map<String, String> rules = configured.get(level.dimension().identifier().toString());
|
||||
if (rules == null || rules.isEmpty()) {
|
||||
return new ApplySummary(1, 0, 0);
|
||||
}
|
||||
|
||||
int applied = 0;
|
||||
int skipped = 0;
|
||||
for (Map.Entry<String, String> entry : rules.entrySet()) {
|
||||
GameRule<?> rule = findRule(entry.getKey());
|
||||
if (rule == null || !rule.isEnabled(level.enabledFeatures())) {
|
||||
LobbySystemsMod.LOGGER.warn("Skipping configured gamerule '{}' for '{}': rule unavailable",
|
||||
entry.getKey(), level.dimension().identifier());
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
if (applySerialized(level, rule, entry.getValue())) {
|
||||
applied++;
|
||||
} else {
|
||||
LobbySystemsMod.LOGGER.warn("Skipping configured gamerule '{}'='{}' for '{}': invalid value",
|
||||
entry.getKey(), entry.getValue(), level.dimension().identifier());
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
return new ApplySummary(1, applied, skipped);
|
||||
}
|
||||
|
||||
public static GameRule<?> findRule(String rawId) {
|
||||
String normalized = rawId.contains(":") ? rawId : "minecraft:" + rawId;
|
||||
for (GameRule<?> rule : BuiltInRegistries.GAME_RULE) {
|
||||
Identifier id = rule.getIdentifierWithFallback();
|
||||
if (id.toString().equals(normalized) || rule.id().equals(rawId)) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean applySerialized(ServerLevel level, GameRule<?> rawRule, String serializedValue) {
|
||||
return applySerializedTyped(level, rawRule, serializedValue);
|
||||
}
|
||||
|
||||
private static <T> boolean applySerializedTyped(ServerLevel level, GameRule<T> rule, String serializedValue) {
|
||||
var result = rule.deserialize(serializedValue).result();
|
||||
if (result.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
level.getGameRules().set(rule, result.get(), level.getServer());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static <T> void resetToDefault(ServerLevel level, GameRule<T> rule) {
|
||||
level.getGameRules().set(rule, rule.defaultValue(), level.getServer());
|
||||
}
|
||||
|
||||
public record ApplySummary(int dimensions, int applied, int skipped) {}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
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.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLevelEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
@@ -39,6 +41,7 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
private static final PortalManager PORTALS = new PortalManager();
|
||||
private static final TeleportCoordinator TELEPORTS = new TeleportCoordinator();
|
||||
private static final DimensionGameModeManager DIMENSION_GAMEMODES = new DimensionGameModeManager();
|
||||
private static final DimensionGameRuleManager DIMENSION_GAMERULES = new DimensionGameRuleManager();
|
||||
private static int saveTicker;
|
||||
|
||||
@Override
|
||||
@@ -76,6 +79,36 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
"Configured dimensions: " + DIMENSION_GAMEMODES.all().size()
|
||||
);
|
||||
|
||||
boolean gameRulesLoaded = DIMENSION_GAMERULES.load();
|
||||
LobbyLog.state(
|
||||
gameRulesLoaded ? "DIMENSION GAMERULES LOADED" : "DIMENSION GAMERULES LOAD FAILED",
|
||||
"File: " + ConfigPaths.DIMENSION_GAMERULES,
|
||||
"Configured dimensions: " + DIMENSION_GAMERULES.all().size(),
|
||||
"Mode: native per-dimension"
|
||||
);
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||
DimensionGameRuleManager.ApplySummary summary = DIMENSION_GAMERULES.applyAll(server);
|
||||
LobbyLog.state(
|
||||
"DIMENSION GAMERULES APPLIED",
|
||||
"Configured dimensions loaded: " + summary.dimensions(),
|
||||
"Rules applied: " + summary.applied(),
|
||||
"Rules skipped: " + summary.skipped()
|
||||
);
|
||||
});
|
||||
|
||||
ServerLevelEvents.LOAD.register((server, world) -> {
|
||||
DimensionGameRuleManager.ApplySummary summary = DIMENSION_GAMERULES.applyToLevel(world);
|
||||
if (summary.applied() > 0 || summary.skipped() > 0) {
|
||||
LobbyLog.state(
|
||||
"DIMENSION GAMERULES WORLD LOAD",
|
||||
"Dimension: " + world.dimension().identifier(),
|
||||
"Rules applied: " + summary.applied(),
|
||||
"Rules skipped: " + summary.skipped()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((listener, sender, server) -> {
|
||||
ServerPlayer player = listener.getPlayer();
|
||||
server.execute(() -> {
|
||||
@@ -149,6 +182,34 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
true,
|
||||
true))))
|
||||
.then(Commands.literal("gamerule")
|
||||
.then(Commands.literal("set")
|
||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
||||
.then(Commands.argument("rule", IdentifierArgument.id())
|
||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||
BuiltInRegistries.GAME_RULE.stream().map(GameRule::getIdentifierWithFallback), builder))
|
||||
.then(Commands.argument("value", StringArgumentType.word())
|
||||
.suggests((ctx, builder) -> suggestGameRuleValues(
|
||||
IdentifierArgument.getId(ctx, "rule").toString(), builder))
|
||||
.executes(ctx -> setDimensionGameRule(
|
||||
ctx.getSource(),
|
||||
ctx.getSource().getServer(),
|
||||
IdentifierArgument.getId(ctx, "dimension").toString(),
|
||||
IdentifierArgument.getId(ctx, "rule").toString(),
|
||||
StringArgumentType.getString(ctx, "value")))))))
|
||||
.then(Commands.literal("reset")
|
||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
||||
.then(Commands.argument("rule", IdentifierArgument.id())
|
||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||
BuiltInRegistries.GAME_RULE.stream().map(GameRule::getIdentifierWithFallback), builder))
|
||||
.executes(ctx -> resetDimensionGameRule(
|
||||
ctx.getSource(),
|
||||
ctx.getSource().getServer(),
|
||||
IdentifierArgument.getId(ctx, "dimension").toString(),
|
||||
IdentifierArgument.getId(ctx, "rule").toString())))))
|
||||
.then(Commands.argument("dimension", IdentifierArgument.id())
|
||||
.suggests((ctx, builder) -> SharedSuggestionProvider.suggestResource(
|
||||
ctx.getSource().getServer().levelKeys().stream().map(ResourceKey::identifier), builder))
|
||||
@@ -300,6 +361,14 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
DIMENSION_GAMEMODES.applyToDimension(server, dimension);
|
||||
}
|
||||
|
||||
boolean gameRulesOk = DIMENSION_GAMERULES.load();
|
||||
if (!gameRulesOk) {
|
||||
source.sendFailure(Component.literal("§eConfig utama direload, tetapi dimension-gamerules.json bermasalah. Cek console."));
|
||||
LobbyLog.state("RELOAD FAILED", "Reason: dimension-gamerules.json gagal dibaca");
|
||||
return 0;
|
||||
}
|
||||
DimensionGameRuleManager.ApplySummary gameRuleSummary = DIMENSION_GAMERULES.applyAll(server);
|
||||
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
player.sendSystemMessage(Component.literal(
|
||||
"§aLobby Systems direload. §7Lobby: §f" + CONFIG.get().lobbyDimension
|
||||
@@ -312,6 +381,7 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
"Minimum play: " + CONFIG.get().minimumPlaySeconds + "s",
|
||||
"Portals: " + PORTALS.size(),
|
||||
"Dimension gamemodes: " + DIMENSION_GAMEMODES.all().size(),
|
||||
"Dimension gamerules: " + DIMENSION_GAMERULES.all().size() + " dimensions / " + gameRuleSummary.applied() + " rules applied",
|
||||
"Player positions lobby exclusion: active"
|
||||
);
|
||||
return 1;
|
||||
@@ -362,11 +432,13 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
int start = (page - 1) * GAMERULES_PER_PAGE;
|
||||
int end = Math.min(start + GAMERULES_PER_PAGE, entries.size());
|
||||
|
||||
sendCommandMessage(source, Component.literal("§8§m----------------------------------------"));
|
||||
sendCommandMessage(source, Component.literal(
|
||||
"§6§lLobby Systems Gamerules §r§7[§f" + page + "§7/§f" + totalPages + "§7]"
|
||||
+ "\n§7Dimension: §f" + level.dimension().identifier()
|
||||
+ " §8| §7Rules: §f" + entries.size()
|
||||
+ (skippedRules > 0 ? " §8| §7Skipped: §e" + skippedRules : "")));
|
||||
sendCommandMessage(source, Component.literal("§8§m----------------------------------------"));
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
GameRuleEntry entry = entries.get(i);
|
||||
@@ -374,10 +446,13 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
sendCommandMessage(source, Component.literal("§8• §f" + entry.name() + " §7= " + valueColor + entry.value()));
|
||||
}
|
||||
|
||||
sendCommandMessage(source, Component.literal("§8§m----------------------------------------"));
|
||||
if (page < totalPages) {
|
||||
int next = page + 1;
|
||||
sendCommandMessage(source, Component.literal(
|
||||
"§7Halaman selanjutnya: §f/ls gamerule " + level.dimension().identifier() + " --page " + next));
|
||||
} else if (page > 1) {
|
||||
sendCommandMessage(source, Component.literal("§7Halaman terakhir."));
|
||||
}
|
||||
return entries.size();
|
||||
}
|
||||
@@ -395,6 +470,106 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
}
|
||||
}
|
||||
|
||||
private static java.util.concurrent.CompletableFuture<com.mojang.brigadier.suggestion.Suggestions> suggestGameRuleValues(
|
||||
String ruleId,
|
||||
com.mojang.brigadier.suggestion.SuggestionsBuilder builder) {
|
||||
GameRule<?> rule = DimensionGameRuleManager.findRule(ruleId);
|
||||
if (rule != null && rule.valueClass() == Boolean.class) {
|
||||
return SharedSuggestionProvider.suggest(new String[]{"true", "false"}, builder);
|
||||
}
|
||||
if (rule != null) {
|
||||
return SharedSuggestionProvider.suggest(new String[]{String.valueOf(rule.defaultValue())}, builder);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static int setDimensionGameRule(
|
||||
CommandSourceStack source,
|
||||
MinecraftServer server,
|
||||
String dimensionId,
|
||||
String ruleId,
|
||||
String value) {
|
||||
ServerLevel level = findLevel(server, dimensionId);
|
||||
if (level == null) {
|
||||
source.sendFailure(Component.literal("§cDimension '" + dimensionId + "' tidak ditemukan atau tidak ter-load."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
GameRule<?> rule = DimensionGameRuleManager.findRule(ruleId);
|
||||
if (rule == null || !rule.isEnabled(level.enabledFeatures())) {
|
||||
source.sendFailure(Component.literal("§cGamerule '" + ruleId + "' tidak ditemukan/tersedia di dimension ini."));
|
||||
return 0;
|
||||
}
|
||||
if (!DimensionGameRuleManager.applySerialized(level, rule, value)) {
|
||||
source.sendFailure(Component.literal("§cValue '" + value + "' tidak valid untuk gamerule " + rule.getIdentifierWithFallback() + "."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
String normalizedDimension = level.dimension().identifier().toString();
|
||||
String normalizedRule = rule.getIdentifierWithFallback().toString();
|
||||
String actualValue = gameRuleValueUnchecked(level, rule);
|
||||
if (!DIMENSION_GAMERULES.set(normalizedDimension, normalizedRule, actualValue)) {
|
||||
source.sendFailure(Component.literal("§cGamerule sudah diterapkan di memory, tetapi gagal menyimpan dimension-gamerules.json."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
sendCommandMessage(source, Component.literal(
|
||||
"§aGamerule dimension disimpan. §7" + normalizedDimension
|
||||
+ " §8| §f" + normalizedRule + " §7= §f" + actualValue));
|
||||
LobbyLog.state(
|
||||
"DIMENSION GAMERULE UPDATED",
|
||||
"Dimension: " + normalizedDimension,
|
||||
"Rule: " + normalizedRule,
|
||||
"Value: " + actualValue,
|
||||
"Persistent: yes"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int resetDimensionGameRule(
|
||||
CommandSourceStack source,
|
||||
MinecraftServer server,
|
||||
String dimensionId,
|
||||
String ruleId) {
|
||||
ServerLevel level = findLevel(server, dimensionId);
|
||||
if (level == null) {
|
||||
source.sendFailure(Component.literal("§cDimension '" + dimensionId + "' tidak ditemukan atau tidak ter-load."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
GameRule<?> rule = DimensionGameRuleManager.findRule(ruleId);
|
||||
if (rule == null || !rule.isEnabled(level.enabledFeatures())) {
|
||||
source.sendFailure(Component.literal("§cGamerule '" + ruleId + "' tidak ditemukan/tersedia di dimension ini."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
resetGameRuleUnchecked(level, rule);
|
||||
String normalizedDimension = level.dimension().identifier().toString();
|
||||
String normalizedRule = rule.getIdentifierWithFallback().toString();
|
||||
boolean removed = DIMENSION_GAMERULES.remove(normalizedDimension, normalizedRule);
|
||||
String actualValue = gameRuleValueUnchecked(level, rule);
|
||||
|
||||
sendCommandMessage(source, Component.literal(
|
||||
"§aGamerule dimension di-reset ke default. §7" + normalizedDimension
|
||||
+ " §8| §f" + normalizedRule + " §7= §f" + actualValue
|
||||
+ (removed ? "" : " §8(config override sebelumnya tidak ada)")));
|
||||
LobbyLog.state(
|
||||
"DIMENSION GAMERULE RESET",
|
||||
"Dimension: " + normalizedDimension,
|
||||
"Rule: " + normalizedRule,
|
||||
"Default: " + actualValue
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static <T> void resetGameRuleUnchecked(ServerLevel level, GameRule<T> rule) {
|
||||
DimensionGameRuleManager.resetToDefault(level, rule);
|
||||
}
|
||||
|
||||
private static <T> String gameRuleValueUnchecked(ServerLevel level, GameRule<T> 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) {
|
||||
@@ -670,6 +845,10 @@ public final class LobbySystemsMod implements ModInitializer {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ServerLevel findLevelForManager(MinecraftServer server, String dimensionId) {
|
||||
return findLevel(server, dimensionId);
|
||||
}
|
||||
|
||||
private static ServerLevel findLevel(MinecraftServer server, String dimensionId) {
|
||||
try {
|
||||
Identifier id = Identifier.parse(dimensionId.trim());
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package id.dartokloning.lobbysystems.mixin;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Vanilla Minecraft 26.2 stores gamerule values in one server-wide GameRuleMap.
|
||||
* Lobby Systems gives every ServerLevel its own copy so rules can differ by
|
||||
* dimension without leaking into other dimensions.
|
||||
*/
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelGameRulesMixin {
|
||||
@Unique
|
||||
private GameRules lobbySystems$dimensionGameRules;
|
||||
|
||||
@Inject(method = "getGameRules", at = @At("HEAD"), cancellable = true)
|
||||
private void lobbySystems$getDimensionGameRules(CallbackInfoReturnable<GameRules> cir) {
|
||||
if (lobbySystems$dimensionGameRules == null) {
|
||||
ServerLevel self = (ServerLevel) (Object) this;
|
||||
MinecraftServer server = self.getServer();
|
||||
if (server != null) {
|
||||
lobbySystems$dimensionGameRules = server.getGameRules().copy(self.enabledFeatures());
|
||||
}
|
||||
}
|
||||
if (lobbySystems$dimensionGameRules != null) {
|
||||
cir.setReturnValue(lobbySystems$dimensionGameRules);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user