From fc503dcd11710fa1b5b7473b17751f7fc82ddbf8 Mon Sep 17 00:00:00 2001 From: Liyan Zhao Date: Mon, 29 Jul 2024 06:40:01 +0800 Subject: [PATCH] feat: servux setting (#9) * feat: servux setting * feat * feat * feat * feat * feat * feat * feat * feat * feat * feat * feat * feat * i18n self-managed lang file system * feat: Add defaultLanguage setting * feat: i18n * feat: i18n * fix * fix * fix * fix * feat * feat * feat * feat * fix * fix * fix * feat: zh_cn.json * fix * Add Init Handler and Command Provider interfaces * Add params to IServerCommand * fix * fix * fix * Version bump --------- Co-authored-by: Sakura Ryoko --- gradle.properties | 2 +- src/main/java/fi/dy/masa/servux/Servux.java | 24 +- .../masa/servux/commands/CommandProvider.java | 30 +- .../servux/commands/ICommandProvider.java | 9 + .../masa/servux/commands/ServuxCommand.java | 274 +++++++++++++++++- .../dataproviders/DataProviderBase.java | 38 ++- .../dataproviders/DataProviderManager.java | 51 +++- .../dataproviders/EntitiesDataProvider.java | 53 +--- .../servux/dataproviders/IDataProvider.java | 7 +- .../dataproviders/LitematicsDataProvider.java | 89 ++---- .../dataproviders/ServuxConfigProvider.java | 114 ++++---- .../dataproviders/StructureDataProvider.java | 79 ++--- .../dataproviders/TweaksDataProvider.java | 50 +--- .../dy/masa/servux/event/PlayerHandler.java | 1 + .../dy/masa/servux/event/ServerHandler.java | 1 + .../masa/servux/event/ServerInitHandler.java | 35 +++ .../{event => interfaces}/IPlayerManager.java | 4 +- .../servux/interfaces/IServerCommand.java | 16 + .../interfaces/IServerInitDispatcher.java | 6 + .../servux/interfaces/IServerInitHandler.java | 6 + .../{event => interfaces}/IServerManager.java | 4 +- .../servux/mixin/MixinCommandManager.java | 2 +- .../mixin/MixinMinecraftDedicatedServer.java | 27 ++ .../dy/masa/servux/servux/ServerListener.java | 4 + .../masa/servux/servux/ServuxInitHandler.java | 22 ++ .../settings/AbstractServuxSetting.java | 124 ++++++++ .../masa/servux/settings/IServuxSetting.java | 47 +++ .../servux/settings/ServuxBoolSetting.java | 59 ++++ .../servux/settings/ServuxIntSetting.java | 80 +++++ .../servux/settings/ServuxListSetting.java | 85 ++++++ .../settings/ServuxStringListSetting.java | 44 +++ .../servux/settings/ServuxStringSetting.java | 56 ++++ .../fi/dy/masa/servux/util/StringUtils.java | 25 ++ .../java/fi/dy/masa/servux/util/i18nLang.java | 140 +++++++++ .../resources/assets/servux/lang/en_us.json | 64 ++++ .../resources/assets/servux/lang/zh_cn.json | 64 ++++ src/main/resources/mixins.servux.json | 1 + 37 files changed, 1445 insertions(+), 292 deletions(-) create mode 100644 src/main/java/fi/dy/masa/servux/commands/ICommandProvider.java create mode 100644 src/main/java/fi/dy/masa/servux/event/ServerInitHandler.java rename src/main/java/fi/dy/masa/servux/{event => interfaces}/IPlayerManager.java (63%) create mode 100644 src/main/java/fi/dy/masa/servux/interfaces/IServerCommand.java create mode 100644 src/main/java/fi/dy/masa/servux/interfaces/IServerInitDispatcher.java create mode 100644 src/main/java/fi/dy/masa/servux/interfaces/IServerInitHandler.java rename src/main/java/fi/dy/masa/servux/{event => interfaces}/IServerManager.java (63%) create mode 100644 src/main/java/fi/dy/masa/servux/mixin/MixinMinecraftDedicatedServer.java create mode 100644 src/main/java/fi/dy/masa/servux/servux/ServuxInitHandler.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/AbstractServuxSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/IServuxSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/ServuxBoolSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/ServuxIntSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/ServuxListSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/ServuxStringListSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/settings/ServuxStringSetting.java create mode 100644 src/main/java/fi/dy/masa/servux/util/i18nLang.java create mode 100644 src/main/resources/assets/servux/lang/en_us.json create mode 100644 src/main/resources/assets/servux/lang/zh_cn.json diff --git a/gradle.properties b/gradle.properties index 02078e9..ac2003a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ author = masa mod_file_name = servux-fabric # Current mod version -mod_version = 0.3.6 +mod_version = 0.3.7 # The Minecraft version that gets set in the built file name minecraft_version_out = 1.21 diff --git a/src/main/java/fi/dy/masa/servux/Servux.java b/src/main/java/fi/dy/masa/servux/Servux.java index 8e18975..ec7c608 100644 --- a/src/main/java/fi/dy/masa/servux/Servux.java +++ b/src/main/java/fi/dy/masa/servux/Servux.java @@ -3,11 +3,11 @@ import net.fabricmc.api.ModInitializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import fi.dy.masa.servux.dataproviders.*; -import fi.dy.masa.servux.event.PlayerHandler; -import fi.dy.masa.servux.servux.PlayerListener; -import fi.dy.masa.servux.event.ServerHandler; -import fi.dy.masa.servux.servux.ServerListener; +import fi.dy.masa.servux.commands.CommandProvider; +import fi.dy.masa.servux.commands.ServuxCommand; +import fi.dy.masa.servux.dataproviders.ServuxConfigProvider; +import fi.dy.masa.servux.event.ServerInitHandler; +import fi.dy.masa.servux.servux.ServuxInitHandler; public class Servux implements ModInitializer { @@ -16,17 +16,9 @@ public class Servux implements ModInitializer @Override public void onInitialize() { - DataProviderManager.INSTANCE.registerDataProvider(ServuxConfigProvider.INSTANCE); - DataProviderManager.INSTANCE.registerDataProvider(LitematicsDataProvider.INSTANCE); - DataProviderManager.INSTANCE.registerDataProvider(EntitiesDataProvider.INSTANCE); - DataProviderManager.INSTANCE.registerDataProvider(StructureDataProvider.INSTANCE); - //DataProviderManager.INSTANCE.registerDataProvider(TweaksDataProvider.INSTANCE); - - ServerListener serverListener = new ServerListener(); - ServerHandler.getInstance().registerServerHandler(serverListener); - - PlayerListener playerListener = new PlayerListener(); - PlayerHandler.getInstance().registerPlayerHandler(playerListener); + ServerInitHandler.getInstance().registerServerInitHandler(new ServuxInitHandler()); + CommandProvider.getInstance().registerCommand(new ServuxCommand()); + // Command Manager gets called before the Init Manager onServerInit() } public static void debugLog(String msg, Object... args) diff --git a/src/main/java/fi/dy/masa/servux/commands/CommandProvider.java b/src/main/java/fi/dy/masa/servux/commands/CommandProvider.java index 316662f..43101aa 100644 --- a/src/main/java/fi/dy/masa/servux/commands/CommandProvider.java +++ b/src/main/java/fi/dy/masa/servux/commands/CommandProvider.java @@ -1,22 +1,40 @@ package fi.dy.masa.servux.commands; +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.command.CommandRegistryAccess; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import fi.dy.masa.servux.interfaces.IServerCommand; -public class CommandProvider +public class CommandProvider implements ICommandProvider { private static final CommandProvider INSTANCE = new CommandProvider(); - public static CommandProvider getInstance() { return INSTANCE; } + private final List commands = new ArrayList<>(); + public static ICommandProvider getInstance() { return INSTANCE; } - /** - * The idea here is to eventually create a more Robust Command Provider, using an interface - */ + @Override + public void registerCommand(IServerCommand command) + { + if (!this.commands.contains(command)) + { + this.commands.add(command); + } + } + + @Override + public void unregisterCommand(IServerCommand command) + { + this.commands.remove(command); + } + + @ApiStatus.Internal public void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) { - ServuxCommand.register(dispatcher); + this.commands.forEach((command) -> command.register(dispatcher, registryAccess, environment)); } } diff --git a/src/main/java/fi/dy/masa/servux/commands/ICommandProvider.java b/src/main/java/fi/dy/masa/servux/commands/ICommandProvider.java new file mode 100644 index 0000000..e3e3429 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/commands/ICommandProvider.java @@ -0,0 +1,9 @@ +package fi.dy.masa.servux.commands; + +import fi.dy.masa.servux.interfaces.IServerCommand; + +public interface ICommandProvider +{ + void registerCommand(IServerCommand command); + void unregisterCommand(IServerCommand command); +} diff --git a/src/main/java/fi/dy/masa/servux/commands/ServuxCommand.java b/src/main/java/fi/dy/masa/servux/commands/ServuxCommand.java index 5c19c9b..fbaee9e 100644 --- a/src/main/java/fi/dy/masa/servux/commands/ServuxCommand.java +++ b/src/main/java/fi/dy/masa/servux/commands/ServuxCommand.java @@ -1,15 +1,41 @@ package fi.dy.masa.servux.commands; -import com.mojang.brigadier.CommandDispatcher; +import java.util.*; + import me.lucko.fabric.api.permissions.v0.Permissions; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.CommandSource; +import net.minecraft.command.argument.IdentifierArgumentType; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; import fi.dy.masa.servux.Reference; +import fi.dy.masa.servux.dataproviders.DataProviderManager; +import fi.dy.masa.servux.dataproviders.IDataProvider; import fi.dy.masa.servux.dataproviders.ServuxConfigProvider; +import fi.dy.masa.servux.interfaces.IServerCommand; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.util.StringUtils; -public class ServuxCommand +public class ServuxCommand implements IServerCommand { - public static void register(CommandDispatcher dispatcher) + public static final ServuxCommand INSTANCE = new ServuxCommand(); + + @Override + public void register(CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess, + CommandManager.RegistrationEnvironment environment) { dispatcher.register(CommandManager .literal(Reference.MOD_ID).requires(Permissions.require(Reference.MOD_ID + ".commands", 4)) @@ -25,6 +51,248 @@ public static void register(CommandDispatcher dispatcher) ServuxConfigProvider.INSTANCE.doSaveConfig(ctx.getSource()); return 1; })) + .then(CommandManager.literal("set") + .requires(Permissions.require(Reference.MOD_ID + ".commands.set", 4)) + .then(settingsNode().then(CommandManager.argument("value", StringArgumentType.greedyString()) + .suggests((ctx, builder) -> { + Identifier settingId = ctx.getArgument("setting", Identifier.class); + String settingName = StringUtils.removeDefaultMinecraftNamespace(settingId); + var setting = DataProviderManager.INSTANCE.getSettingByName(settingName); + if (setting != null) + { + return CommandSource.suggestMatching(setting.examples(), builder); + } + return builder.buildFuture(); + }) + .executes(ServuxCommand::configModify)))) + .then(CommandManager.literal("info") + .requires(Permissions.require(Reference.MOD_ID + ".commands.info", 4)) + .then(settingsNode().executes(ServuxCommand::configInfo))) + .then(CommandManager.literal("list") + .requires(Permissions.require(Reference.MOD_ID + ".commands.list", 4)) + .executes(ctx -> configList(ctx, DataProviderManager.INSTANCE.getAllProviders().stream() + .flatMap(iDataProvider -> iDataProvider.getSettings().stream()).toList())) + .then(CommandManager.argument("provider", StringArgumentType.string()) + .suggests((ctx, builder) -> CommandSource.suggestMatching(DataProviderManager.INSTANCE.getAllProviders(), builder, IDataProvider::getName, iDataProvider -> Text.literal(iDataProvider.getDescription()).append(StringUtils.translate("servux.suffix.data_provider")))) + .executes(ctx -> { + String provider = StringArgumentType.getString(ctx, "provider"); + Optional dataProvider = DataProviderManager.INSTANCE.getProviderByName(provider); + if (dataProvider.isEmpty()) + { + throw StringUtils.translateError("servux.command.error.unknown_data_provider"); + } + ctx.getSource().sendFeedback(() -> StringUtils.translate("servux.command.config.list.data_provider", provider), false); + return configList(ctx, dataProvider.get().getSettings()); + }))) + .then(CommandManager.literal("search") + .requires(Permissions.require(Reference.MOD_ID + ".commands.list", 4)) + .then(CommandManager.argument("query", StringArgumentType.greedyString()) + .executes(ctx -> + { + String query = StringArgumentType.getString(ctx, "query"); + var settings = configSearch(ctx, query); + if (settings.isEmpty()) + { + ctx.getSource().sendFeedback(() -> StringUtils.translate("servux.command.search.none", query), false); + return 0; + } + else + { + ctx.getSource().sendFeedback(() -> StringUtils.translate("servux.command.search.results", settings.size(), query), false); + return configList(ctx, settings); + } + }))) + ); + } + + private List> configSearch(CommandContext ctx, String query) + { + String[] searchParts = query.split(" "); + return DataProviderManager.INSTANCE.getAllProviders().stream() + .flatMap(iDataProvider -> iDataProvider.getSettings().stream()) + .filter(iServuxSetting -> + { + for (String part : searchParts) + { + if (iServuxSetting.name().contains(part)) + { + continue; + } + if (iServuxSetting.comment().getString().contains(part)) + { + continue; + } + if (iServuxSetting.dataProvider().getName().contains(part)) + { + continue; + } + return false; + } + return true; + }).toList(); + } + + private int configList(CommandContext ctx, List> list) + { + if (list.isEmpty()) + { + ctx.getSource().sendFeedback(() -> StringUtils.translate("servux.command.error.no_settings"), false); + return 0; + } + + Set appearedNames = new HashSet<>(); + Set appearedMultiTimes = new HashSet<>(); + for (IServuxSetting setting : list) + { + if (!appearedNames.add(setting.name())) + { + appearedMultiTimes.add(setting.name()); + } + } + + for (IServuxSetting setting : list) + { + ctx.getSource().sendFeedback(() -> + { + MutableText text = Text.empty(); + text.append(setting.shortDisplayName().copy().styled(style -> style + .withBold(true) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/servux info " + setting.qualifiedName())))); + if (appearedMultiTimes.contains(setting.name())) + { + text.append(Text.literal(" (").append(Text.of(setting.dataProvider().getName())).append(")").formatted(Formatting.GRAY)); + } + String value = setting.valueToString(setting.getValue()); + if (value.length() < 10) + { + text.append(": ").append(value); + } + return text; + }, false); + } + return list.size(); + } + + private ArgumentBuilder settingsNode() { + var node = CommandManager.argument("setting", IdentifierArgumentType.identifier()); + node.suggests((ctx, builder) -> { + if (builder.getRemainingLowerCase().contains(":")) + { + String providerName = builder.getRemaining().split(":")[0]; + DataProviderManager.INSTANCE.getProviderByName(providerName).ifPresent(iDataProvider -> + iDataProvider.getSettings().forEach(iServuxSetting -> + { + builder.suggest(providerName + ":" + iServuxSetting.name(), iServuxSetting.prettyName()); + })); + } + else + { + CommandSource.suggestMatching(DataProviderManager.INSTANCE.getAllProviders().stream() + .flatMap(iDataProvider -> iDataProvider.getSettings().stream()).toList(), builder, IServuxSetting::name, IServuxSetting::prettyName); + + CommandSource.suggestMatching(DataProviderManager.INSTANCE.getAllProviders(), builder, IDataProvider::getName, iDataProvider -> Text.literal(iDataProvider.getDescription()).append(StringUtils.translate("servux.suffix.data_provider"))); + } + return builder.buildFuture(); + }); + return node; + } + + private static int configInfo(CommandContext ctx) throws CommandSyntaxException + { + Identifier settingId = ctx.getArgument("setting", Identifier.class); + String settingName = StringUtils.removeDefaultMinecraftNamespace(settingId); + var setting = DataProviderManager.INSTANCE.getSettingByName(settingName); + if (setting == null) + { + throw StringUtils.translateError("servux.command.error.unknown_setting"); + } + + ctx.getSource().sendFeedback(Text::empty, false); + ctx.getSource().sendFeedback(() -> + { + MutableText text = Text.empty(); + text.append(setting.prettyName().copy().styled(style -> + style.withColor(Formatting.YELLOW).withBold(true))); + text.append(" ("); + text.append(Text.literal(setting.qualifiedName()).styled(style -> + style.withColor(Formatting.GRAY) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, StringUtils.translate("servux.command.info.click_to_copy"))) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, setting.qualifiedName())) + )); + text.append(")"); + return text; + }, false); + ctx.getSource().sendFeedback(() -> setting.comment().copy().formatted(Formatting.GRAY), false); + ctx.getSource().sendFeedback(() -> + { + MutableText text = StringUtils.translate("servux.command.info.value", setting.valueToString(setting.getValue())).styled(style -> style + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, StringUtils.translate("servux.command.info.click_to_set", setting.prettyName()))) + .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/servux set " + setting.qualifiedName() + " ")) + ).append(" "); + if (Objects.equals(setting.getDefaultValue(), setting.getValue())) + { + text.append(StringUtils.translate("servux.command.suffix.default_value").formatted(Formatting.GRAY)); + } + else + { + text.append(StringUtils.translate("servux.command.suffix.modified").formatted(Formatting.GREEN)); + text.append(" "); + text.append(StringUtils.translate("servux.command.info.reset").formatted(Formatting.GRAY) + .styled(style -> style + .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/servux set " + setting.qualifiedName() + " " + setting.valueToString(setting.getDefaultValue()))) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, StringUtils.translate("servux.command.info.click_to_reset_to", setting.valueToString(setting.getDefaultValue())))) + )); + } + return text; + }, false); + if (!setting.examples().isEmpty()) + { + MutableText text = StringUtils.translate("servux.command.info.examples"); + setting.examples().forEach(example -> + { + MutableText optionText = Text.literal(example).styled(style -> { + if (example.equals(setting.valueToString(setting.getValue()))) + { + style = style.withColor(Formatting.GREEN); + } + else + { + style = style.withColor(Formatting.GRAY); + } + return style + .withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/servux set " + setting.qualifiedName() + " " + example)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, StringUtils.translate("servux.command.info.click_to_set", example))); + }); + text.append(optionText).append(" "); + }); + ctx.getSource().sendFeedback(() -> text, false); + } + + return 1; + } + + private static int configModify(CommandContext ctx) throws CommandSyntaxException + { + Identifier settingId = ctx.getArgument("setting", Identifier.class); + String settingName = StringUtils.removeDefaultMinecraftNamespace(settingId); + var setting = DataProviderManager.INSTANCE.getSettingByName(settingName); + if (setting == null) + { + throw StringUtils.translateError("servux.command.error.unknown_setting"); + } + String value = ctx.getArgument("value", String.class); + if (!setting.validateString(value)) + { + throw StringUtils.translateError("servux.command.error.invalid_value"); + } + setting.setValueFromString(value); + ctx.getSource().sendFeedback(() -> + StringUtils.translate("servux.command.config.set_value", + setting.shortDisplayName().copy().styled(style -> style + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/servux info " + setting.qualifiedName()))), + value), + true ); + return 1; } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderBase.java b/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderBase.java index 0ceb2f6..5a562e3 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderBase.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderBase.java @@ -1,7 +1,13 @@ package fi.dy.masa.servux.dataproviders; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.util.JsonUtils; import net.minecraft.util.Identifier; +import java.util.List; + public abstract class DataProviderBase implements IDataProvider { protected final Identifier networkChannel; @@ -78,8 +84,38 @@ protected void setTickRate(int tickRate) } @Override - public final int getTickRate() + public final int getTickInterval() { return this.tickRate; } + + @Override + public List> getSettings() + { + return List.of(); + } + + @Override + public JsonObject toJson() + { + JsonObject object = new JsonObject(); + for (IServuxSetting setting : getSettings()) + { + object.add(setting.name(), setting.writeToJson()); + } + return object; + } + + @Override + public void fromJson(JsonObject obj) + { + for (IServuxSetting setting : getSettings()) + { + JsonElement element = obj.get(setting.name()); + if (element != null) + { + setting.readFromJson(element); + } + } + } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderManager.java b/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderManager.java index d5295d1..298e541 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderManager.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/DataProviderManager.java @@ -1,8 +1,10 @@ package fi.dy.masa.servux.dataproviders; +import javax.annotation.Nullable; import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.Optional; import com.google.common.collect.ImmutableList; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -10,12 +12,16 @@ import net.minecraft.server.MinecraftServer; import fi.dy.masa.servux.Reference; import fi.dy.masa.servux.Servux; +import fi.dy.masa.servux.settings.IServuxSetting; import fi.dy.masa.servux.util.JsonUtils; public class DataProviderManager { public static final DataProviderManager INSTANCE = new DataProviderManager(); + /** + * lower case name to data provider instances. + */ protected final HashMap providers = new HashMap<>(); protected ImmutableList providersImmutable = ImmutableList.of(); protected ArrayList providersTicking = new ArrayList<>(); @@ -33,7 +39,7 @@ public ImmutableList getAllProviders() */ public boolean registerDataProvider(IDataProvider provider) { - String name = provider.getName(); + String name = provider.getName().toLowerCase(); if (this.providers.containsKey(name) == false) { @@ -84,7 +90,7 @@ public void tickProviders(MinecraftServer server, int tickCounter) { for (IDataProvider provider : this.providersTicking) { - if ((tickCounter % provider.getTickRate()) == 0) + if ((tickCounter % provider.getTickInterval()) == 0) { provider.tick(server, tickCounter); } @@ -128,6 +134,47 @@ public void onServerTickEndPost() } } + public Optional getProviderByName(String providerName) + { + return Optional.ofNullable(this.providers.get(providerName)); + } + + public @Nullable IServuxSetting getSettingByName(String name) + { + if (name.contains(":")) + { + String[] parts = name.split(":"); + String providerName = parts[0]; + String settingName = parts[1]; + IDataProvider provider = this.providers.get(providerName); + + if (provider != null) + { + for (IServuxSetting setting : provider.getSettings()) + { + if (setting.name().equalsIgnoreCase(settingName)) + { + return setting; + } + } + } + } + else + { + for (IDataProvider provider : this.providersImmutable) + { + for (IServuxSetting setting : provider.getSettings()) + { + if (setting.name().equalsIgnoreCase(name)) + { + return setting; + } + } + } + } + return null; + } + public void readFromConfig() { JsonElement el = JsonUtils.parseJsonFile(this.getConfigFile()); diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/EntitiesDataProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/EntitiesDataProvider.java index 751efe4..ede6743 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/EntitiesDataProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/EntitiesDataProvider.java @@ -1,7 +1,6 @@ package fi.dy.masa.servux.dataproviders; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; +import java.util.List; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.Entity; @@ -14,14 +13,18 @@ import fi.dy.masa.servux.network.ServerPlayHandler; import fi.dy.masa.servux.network.packet.ServuxEntitiesHandler; import fi.dy.masa.servux.network.packet.ServuxEntitiesPacket; -import fi.dy.masa.servux.util.JsonUtils; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.settings.ServuxIntSetting; public class EntitiesDataProvider extends DataProviderBase { public static final EntitiesDataProvider INSTANCE = new EntitiesDataProvider(); protected final static ServuxEntitiesHandler HANDLER = ServuxEntitiesHandler.getInstance(); protected final NbtCompound metadata = new NbtCompound(); - protected int permissionLevel = -1; + protected ServuxIntSetting permissionLevel = new ServuxIntSetting(this, + "permission_level", + 0, 4, 0); + private List> settings = List.of(this.permissionLevel); protected EntitiesDataProvider() { @@ -37,6 +40,12 @@ protected EntitiesDataProvider() this.metadata.putString("servux", Reference.MOD_STRING); } + @Override + public List> getSettings() + { + return settings; + } + @Override public void registerHandler() { @@ -129,18 +138,10 @@ public void handleBulkClientRequest(ServerPlayerEntity player, int transactionId // todo } - protected void setPermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - this.permissionLevel = level; - } - } - @Override public boolean hasPermission(ServerPlayerEntity player) { - return Permissions.check(player, this.permNode, this.permissionLevel > -1 ? this.permissionLevel : this.defaultPerm); + return Permissions.check(player, this.permNode, this.permissionLevel.getValue()); } @Override @@ -154,30 +155,4 @@ public void onTickEndPost() { // NO-OP } - - @Override - public JsonObject toJson() - { - JsonObject obj = new JsonObject(); - - if (this.permissionLevel > -1) - { - obj.add("permission_level", new JsonPrimitive(this.permissionLevel)); - } - else - { - obj.add("permission_level", new JsonPrimitive(this.defaultPerm)); - } - - return obj; - } - - @Override - public void fromJson(JsonObject obj) - { - if (JsonUtils.hasInteger(obj, "permission_level")) - { - this.setPermissionLevel(JsonUtils.getInteger(obj, "permission_level")); - } - } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/IDataProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/IDataProvider.java index f3cb408..3a3e3c7 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/IDataProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/IDataProvider.java @@ -1,11 +1,14 @@ package fi.dy.masa.servux.dataproviders; import com.google.gson.JsonObject; +import fi.dy.masa.servux.settings.IServuxSetting; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; import fi.dy.masa.servux.network.IPluginServerPlayHandler; +import java.util.List; + public interface IDataProvider { /** @@ -91,7 +94,7 @@ default boolean shouldTick() * Returns the interval in game ticks that this data provider should be ticked at * @return */ - int getTickRate(); + int getTickInterval(); /** * Called at the given tick rate @@ -129,4 +132,6 @@ default void tick(MinecraftServer server, int tickCounter) JsonObject toJson(); void fromJson(JsonObject obj); + + List> getSettings(); } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/LitematicsDataProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/LitematicsDataProvider.java index cfa4619..8e4a00d 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/LitematicsDataProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/LitematicsDataProvider.java @@ -1,10 +1,10 @@ package fi.dy.masa.servux.dataproviders; -import java.util.ArrayList; import java.util.List; import java.util.Set; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; + +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.settings.ServuxIntSetting; import fi.dy.masa.servux.util.*; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.block.entity.BlockEntity; @@ -13,7 +13,6 @@ import net.minecraft.nbt.NbtList; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.Vec3d; @@ -25,17 +24,20 @@ import fi.dy.masa.servux.network.ServerPlayHandler; import fi.dy.masa.servux.network.packet.ServuxLitematicaHandler; import fi.dy.masa.servux.network.packet.ServuxLitematicaPacket; -import fi.dy.masa.servux.schematic.LitematicaSchematic; import fi.dy.masa.servux.schematic.placement.SchematicPlacement; -import fi.dy.masa.servux.schematic.selection.Box; public class LitematicsDataProvider extends DataProviderBase { public static final LitematicsDataProvider INSTANCE = new LitematicsDataProvider(); protected final static ServuxLitematicaHandler HANDLER = ServuxLitematicaHandler.getInstance(); protected final NbtCompound metadata = new NbtCompound(); - protected int permissionLevel = -1; - protected int pastePermissionLevel = -1; + protected ServuxIntSetting permissionLevel = new ServuxIntSetting(this, + "permission_level", + 0, 4, 0); + protected ServuxIntSetting pastePermissionLevel = new ServuxIntSetting(this, + "permission_level_paste", + 0, 4, 0); + private final List> settings = List.of(this.permissionLevel, this.pastePermissionLevel); protected LitematicsDataProvider() { @@ -51,6 +53,12 @@ protected LitematicsDataProvider() this.metadata.putString("servux", Reference.MOD_STRING); } + @Override + public List> getSettings() + { + return settings; + } + @Override public void registerHandler() { @@ -212,13 +220,13 @@ public void handleClientPasteRequest(ServerPlayerEntity player, int transactionI if (this.hasPermission(player) == false || this.hasPermissionsForPaste(player) == false) { Servux.debugLog("litematic_data: Denying Litematic Paste for player {}, Insufficient Permissions.", player.getName().getLiteralString()); - player.sendMessage(Text.literal("§cInsufficient Permissions for the Litematic paste operation.§r")); + player.sendMessage(StringUtils.translate("servux.litematics.error.insufficent_for_paste")); return; } if (player.isCreative() == false) { Servux.debugLog("litematic_data: Denying Litematic Paste for player {}, Player is not in Creative Mode.", player.getName().getLiteralString()); - player.sendMessage(Text.literal("§cCreative Mode is required for the Litematic paste operation.§r")); + player.sendMessage(StringUtils.translate("servux.litematics.error.creative_required")); return; } @@ -230,30 +238,15 @@ public void handleClientPasteRequest(ServerPlayerEntity player, int transactionI ReplaceBehavior replaceMode = ReplaceBehavior.fromStringStatic(tags.getString("ReplaceMode")); placement.pasteTo(player.getServerWorld(), replaceMode); long timeElapsed = System.currentTimeMillis() - timeStart; - player.sendMessage(Text.of("Pasted §b"+placement.getName()+"§r to world §d"+player.getServerWorld().getRegistryKey().getValue().toString()+"§r in §a"+timeElapsed+"§rms."), false); - } - } - - protected void setPermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - this.permissionLevel = level; - } - } - - protected void setPastePermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - this.pastePermissionLevel = level; + //player.sendMessage(Text.of("Pasted §b"+placement.getName()+"§r to world §d"+player.getServerWorld().getRegistryKey().getValue().toString()+"§r in §a"+timeElapsed+"§rms."), false); + player.sendMessage(StringUtils.translate("servux.litematics.success.pasted", placement.getName(), player.getServerWorld().getRegistryKey().getValue().toString(), timeElapsed), false); } } @Override public boolean hasPermission(ServerPlayerEntity player) { - return Permissions.check(player, this.permNode, this.permissionLevel > -1 ? this.permissionLevel : this.defaultPerm); + return Permissions.check(player, this.permNode, this.permissionLevel.getValue()); } @Override @@ -270,44 +263,6 @@ public void onTickEndPost() public boolean hasPermissionsForPaste(ServerPlayerEntity player) { - return (this.hasPermission(player) && Permissions.check(player, this.permNode + ".paste", this.pastePermissionLevel > -1 ? this.pastePermissionLevel : this.defaultPerm)); - } - - @Override - public JsonObject toJson() - { - JsonObject obj = new JsonObject(); - - if (this.permissionLevel > -1) - { - obj.add("permission_level", new JsonPrimitive(this.permissionLevel)); - } - else - { - obj.add("permission_level", new JsonPrimitive(this.defaultPerm)); - } - if (this.pastePermissionLevel > -1) - { - obj.add("permission_level_paste", new JsonPrimitive(this.pastePermissionLevel)); - } - else - { - obj.add("permission_level_paste", new JsonPrimitive(this.defaultPerm)); - } - - return obj; - } - - @Override - public void fromJson(JsonObject obj) - { - if (JsonUtils.hasInteger(obj, "permission_level")) - { - this.setPermissionLevel(JsonUtils.getInteger(obj, "permission_level")); - } - if (JsonUtils.hasInteger(obj, "permission_level_paste")) - { - this.setPastePermissionLevel(JsonUtils.getInteger(obj, "permission_level_paste")); - } + return this.hasPermission(player) && Permissions.check(player, this.permNode + ".paste", this.pastePermissionLevel.getValue()); } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/ServuxConfigProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/ServuxConfigProvider.java index 98df7d7..59742a4 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/ServuxConfigProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/ServuxConfigProvider.java @@ -1,8 +1,11 @@ package fi.dy.masa.servux.dataproviders; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.annotations.SerializedName; +import java.util.List; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import fi.dy.masa.servux.settings.ServuxStringSetting; +import fi.dy.masa.servux.util.i18nLang; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; @@ -10,13 +13,44 @@ import net.minecraft.util.Identifier; import fi.dy.masa.servux.Reference; import fi.dy.masa.servux.network.IPluginServerPlayHandler; - -import java.util.List; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.settings.ServuxBoolSetting; +import fi.dy.masa.servux.settings.ServuxIntSetting; +import fi.dy.masa.servux.util.StringUtils; public class ServuxConfigProvider extends DataProviderBase { public static final ServuxConfigProvider INSTANCE = new ServuxConfigProvider(); - ServuxConfig config = new ServuxConfig(); + + private final ServuxIntSetting basePermissionLevel = new ServuxIntSetting(this, "permission_level", 0, 4, 0); + private final ServuxIntSetting adminPermissionLevel = new ServuxIntSetting(this, "permission_level_admin", 3, 4, 0); + private final ServuxIntSetting easyPlacePermissionLevel = new ServuxIntSetting(this, "permission_level_easy_place", 0, 4, 0); + private final ServuxStringSetting defaultLanguage = new ServuxStringSetting(this, "default_language", i18nLang.DEFAULT_LANG, List.of("en_us", "zh_cn"), false) { + @Override + public void setValueNoCallback(String value) + { + i18nLang.tryLoadLanguage(value.toLowerCase()); + super.setValueNoCallback(value.toLowerCase()); + } + + @Override + public void setValue(String value) throws CommandSyntaxException + { + String lowerCase = value.toLowerCase(); + if (i18nLang.tryLoadLanguage(lowerCase)) + { + var oldValue = this.getValue(); + super.setValueNoCallback(lowerCase); + this.onValueChanged(oldValue, value); + } + else + { + throw new SimpleCommandExceptionType(StringUtils.translate("servux.command.config.invalid_language", value)).create(); + } + } + }; + private final ServuxBoolSetting debugLog = new ServuxBoolSetting(this, "debug_log", Text.of("Debug Log"), Text.of("Enable debug logging"), false); + private final List> settings = List.of(this.basePermissionLevel, this.adminPermissionLevel, this.easyPlacePermissionLevel, this.defaultLanguage, this.debugLog); protected ServuxConfigProvider() { @@ -26,6 +60,12 @@ protected ServuxConfigProvider() "The Servux Main configuration data provider"); } + @Override + public List> getSettings() + { + return settings; + } + @Override public void registerHandler() { @@ -47,47 +87,18 @@ public IPluginServerPlayHandler getPacketHandler() public void doReloadConfig(ServerCommandSource source) { DataProviderManager.INSTANCE.readFromConfig(); - source.sendFeedback(() -> Text.of("Reloaded config!"), true); + source.sendFeedback(() -> StringUtils.translate("servux.command.config.reloaded"), true); } public void doSaveConfig(ServerCommandSource source) { DataProviderManager.INSTANCE.writeToConfig(); - source.sendFeedback(() -> Text.of("Saved config!"), true); + source.sendFeedback(() -> StringUtils.translate("servux.command.config.saved"), true); } public boolean hasDebugMode() { - return config.debugLog; - } - - public void setDebugMode(boolean debugLog) - { - config.debugLog = debugLog; - } - - protected void setBasePermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - config.basePermissionLevel = level; - } - } - - protected void setAdminPermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - config.adminPermissionLevel = level; - } - } - - protected void setPermissionLevel_EasyPlace(int level) - { - if (!(level < 0 || level > 4)) - { - config.easyPlacePermissionLevel = level; - } + return this.debugLog.getValue(); } @Override @@ -98,7 +109,7 @@ public boolean hasPermission(ServerPlayerEntity player) return false; } - return Permissions.check(player, Reference.MOD_ID+".main.admin", config.adminPermissionLevel); + return Permissions.check(player, Reference.MOD_ID+".main.admin", adminPermissionLevel.getValue()); } public boolean hasPermission_EasyPlace(ServerPlayerEntity player) @@ -108,7 +119,7 @@ public boolean hasPermission_EasyPlace(ServerPlayerEntity player) return false; } - return Permissions.check(player, Reference.MOD_ID+".main.easy_place", config.easyPlacePermissionLevel); + return Permissions.check(player, Reference.MOD_ID+".main.easy_place", easyPlacePermissionLevel.getValue()); } @Override @@ -123,27 +134,8 @@ public void onTickEndPost() // NO-OP } - @Override - public JsonObject toJson() - { - return (JsonObject) new Gson().toJsonTree(config); - } - - public static final class ServuxConfig - { - @SerializedName("permission_level") - private int basePermissionLevel = 0; - @SerializedName("permission_level_admin") - private int adminPermissionLevel = 3; - @SerializedName("permission_level_easy_place") - private int easyPlacePermissionLevel = 0; - @SerializedName("debug_log") - private boolean debugLog = false; - } - - @Override - public void fromJson(JsonObject obj) + public String getDefaultLanguage() { - config = new Gson().fromJson(obj, ServuxConfig.class); + return defaultLanguage.getValue(); } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/StructureDataProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/StructureDataProvider.java index 164ede3..9bf040d 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/StructureDataProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/StructureDataProvider.java @@ -3,9 +3,10 @@ import javax.annotation.Nullable; import java.util.*; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.annotations.SerializedName; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.settings.ServuxBoolSetting; +import fi.dy.masa.servux.settings.ServuxIntSetting; +import fi.dy.masa.servux.settings.ServuxStringListSetting; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; @@ -46,7 +47,14 @@ public class StructureDataProvider extends DataProviderBase protected final Map> timeouts = new HashMap<>(); protected final NbtCompound metadata = new NbtCompound(); protected int retainDistance; - private StructureDataProviderConfig config = new StructureDataProviderConfig(); + private ServuxIntSetting permissionLevel = new ServuxIntSetting(this, "permission_level", 0, 4, 0); + private ServuxBoolSetting structureBlacklistEnabled = new ServuxBoolSetting(this, "structures_blacklist_enabled", false); + private ServuxBoolSetting structureWhitelistEnabled = new ServuxBoolSetting(this, "structures_whitelist_enabled", false); + private ServuxStringListSetting structureBlacklist = new ServuxStringListSetting(this, "structures_blacklist", List.of("minecraft:buried_treasure")); + private ServuxStringListSetting structureWhitelist = new ServuxStringListSetting(this, "structures_whitelist", List.of()); + private ServuxIntSetting updateInterval = new ServuxIntSetting(this, "update_interval", 40, 1200, 1); + private ServuxIntSetting timeout = new ServuxIntSetting(this, "timeout", 600, 1200, 40); + private List> settings = List.of(this.permissionLevel, this.structureBlacklistEnabled, this.structureWhitelistEnabled, this.structureBlacklist, this.structureWhitelist, this.updateInterval, this.timeout); // FIXME --> Move out of structures channel in the future private BlockPos spawnPos = BlockPos.ORIGIN; @@ -65,7 +73,7 @@ protected StructureDataProvider() this.metadata.putString("id", this.getNetworkChannel().toString()); this.metadata.putInt("version", this.getProtocolVersion()); this.metadata.putString("servux", Reference.MOD_STRING); - this.metadata.putInt("timeout", config.timeout); + this.metadata.putInt("timeout", timeout.getValue()); // TODO --> Move out of structures channel in the future this.metadata.putInt("spawnPosX", this.getSpawnPos().getX()); @@ -74,6 +82,12 @@ protected StructureDataProvider() this.metadata.putInt("spawnChunkRadius", this.getSpawnChunkRadius()); } + @Override + public List> getSettings() + { + return settings; + } + @Override public void registerHandler() { @@ -108,7 +122,7 @@ public boolean shouldTick() @Override public void tick(MinecraftServer server, int tickCounter) { - if ((tickCounter % config.updateInterval) == 0) + if ((tickCounter % updateInterval.getValue()) == 0) { //Servux.printDebug("=======================\n"); //Servux.printDebug("tick: %d - %s\n", tickCounter, this.isEnabled()); @@ -248,7 +262,7 @@ protected void addChunkTimeoutIfHasReferences(final UUID uuid, WorldChunk chunk, //System.out.printf("addChunkTimeoutIfHasReferences: %s\n", pos); // Set the timeout so it's already expired and will cause the chunk to be sent on the next update tick - map.computeIfAbsent(pos, (p) -> new Timeout(tickCounter - config.timeout)); + map.computeIfAbsent(pos, (p) -> new Timeout(tickCounter - timeout.getValue())); } } @@ -309,7 +323,7 @@ protected void sendAndRefreshExpiredStructures(ServerPlayerEntity player, Map 4)) - { - config.permissionLevel = level; - } - } - @Override public boolean hasPermission(ServerPlayerEntity player) { - return Permissions.check(player, this.permNode, config.permissionLevel); + return Permissions.check(player, this.permNode, permissionLevel.getValue()); } @Override @@ -608,48 +614,17 @@ public void onTickEndPost() // NO-OP } - @Override - public JsonObject toJson() - { - return new Gson().toJsonTree(config).getAsJsonObject(); - } - - public static final class StructureDataProviderConfig - { - @SerializedName("permission_level") - private int permissionLevel = 0; - @SerializedName("structures_blacklist") - private StructureList structuresBlacklist = new StructureList(); - @SerializedName("structures_whitelist") - private StructureList structuresWhitelist = new StructureList(); - @SerializedName("update_interval") - private int updateInterval = 40; - private int timeout = 30 * 20; - - public static class StructureList - { - public boolean enabled = false; - public List structures = List.of(); - } - } - public boolean shouldSendStructure(Identifier identifier) { - if (config.structuresWhitelist.enabled) + if (structureWhitelistEnabled.getValue()) { - return config.structuresWhitelist.structures.contains(identifier.toString()); + return structureWhitelist.getValue().contains(identifier.toString()); } - if (config.structuresBlacklist.enabled) + if (structureBlacklistEnabled.getValue()) { - return !config.structuresBlacklist.structures.contains(identifier.toString()); + return !structureBlacklist.getValue().contains(identifier.toString()); } return true; } - - @Override - public void fromJson(JsonObject obj) - { - config = new Gson().fromJson(obj, StructureDataProviderConfig.class); - } } diff --git a/src/main/java/fi/dy/masa/servux/dataproviders/TweaksDataProvider.java b/src/main/java/fi/dy/masa/servux/dataproviders/TweaksDataProvider.java index 07ed42b..f068869 100644 --- a/src/main/java/fi/dy/masa/servux/dataproviders/TweaksDataProvider.java +++ b/src/main/java/fi/dy/masa/servux/dataproviders/TweaksDataProvider.java @@ -2,11 +2,14 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import fi.dy.masa.servux.settings.IServuxSetting; +import fi.dy.masa.servux.settings.ServuxIntSetting; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.Entity; import net.minecraft.nbt.NbtCompound; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import fi.dy.masa.servux.Reference; import fi.dy.masa.servux.Servux; @@ -16,12 +19,15 @@ import fi.dy.masa.servux.network.packet.ServuxTweaksPacket; import fi.dy.masa.servux.util.JsonUtils; +import java.util.List; + public class TweaksDataProvider extends DataProviderBase { public static final TweaksDataProvider INSTANCE = new TweaksDataProvider(); protected final static ServuxTweaksHandler HANDLER = ServuxTweaksHandler.getInstance(); protected final NbtCompound metadata = new NbtCompound(); - protected int permissionLevel = -1; + protected ServuxIntSetting permissionLevel = new ServuxIntSetting(this, "permission_level", Text.of("Permission Level"), Text.of("The permission level required to access the data provider"), 0, 4, 0); + private List> settings = List.of(this.permissionLevel); protected TweaksDataProvider() { @@ -37,6 +43,12 @@ protected TweaksDataProvider() this.metadata.putString("servux", Reference.MOD_STRING); } + @Override + public List> getSettings() + { + return settings; + } + @Override public void registerHandler() { @@ -139,18 +151,10 @@ public void handleClientBulkData(ServerPlayerEntity player, int transactionId, N // todo } - protected void setPermissionLevel(int level) - { - if (!(level < 0 || level > 4)) - { - this.permissionLevel = level; - } - } - @Override public boolean hasPermission(ServerPlayerEntity player) { - return Permissions.check(player, this.permNode, this.permissionLevel > -1 ? this.permissionLevel : this.defaultPerm); + return Permissions.check(player, this.permNode, this.permissionLevel.getValue()); } @Override @@ -164,30 +168,4 @@ public void onTickEndPost() { // NO-OP } - - @Override - public JsonObject toJson() - { - JsonObject obj = new JsonObject(); - - if (this.permissionLevel > -1) - { - obj.add("permission_level", new JsonPrimitive(this.permissionLevel)); - } - else - { - obj.add("permission_level", new JsonPrimitive(this.defaultPerm)); - } - - return obj; - } - - @Override - public void fromJson(JsonObject obj) - { - if (JsonUtils.hasInteger(obj, "permission_level")) - { - this.setPermissionLevel(JsonUtils.getInteger(obj, "permission_level")); - } - } } diff --git a/src/main/java/fi/dy/masa/servux/event/PlayerHandler.java b/src/main/java/fi/dy/masa/servux/event/PlayerHandler.java index 367b7b5..fba7565 100644 --- a/src/main/java/fi/dy/masa/servux/event/PlayerHandler.java +++ b/src/main/java/fi/dy/masa/servux/event/PlayerHandler.java @@ -10,6 +10,7 @@ import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import fi.dy.masa.servux.interfaces.IPlayerListener; +import fi.dy.masa.servux.interfaces.IPlayerManager; public class PlayerHandler implements IPlayerManager { diff --git a/src/main/java/fi/dy/masa/servux/event/ServerHandler.java b/src/main/java/fi/dy/masa/servux/event/ServerHandler.java index 8fb54a0..ee1f738 100644 --- a/src/main/java/fi/dy/masa/servux/event/ServerHandler.java +++ b/src/main/java/fi/dy/masa/servux/event/ServerHandler.java @@ -6,6 +6,7 @@ import net.minecraft.resource.ResourceManager; import net.minecraft.server.MinecraftServer; import fi.dy.masa.servux.interfaces.IServerListener; +import fi.dy.masa.servux.interfaces.IServerManager; /** * Interface Handler for Server loading / unloading events --> similar to WorldLoadHandler, diff --git a/src/main/java/fi/dy/masa/servux/event/ServerInitHandler.java b/src/main/java/fi/dy/masa/servux/event/ServerInitHandler.java new file mode 100644 index 0000000..ef53c0b --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/event/ServerInitHandler.java @@ -0,0 +1,35 @@ +package fi.dy.masa.servux.event; + +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import fi.dy.masa.servux.interfaces.IServerInitDispatcher; +import fi.dy.masa.servux.interfaces.IServerInitHandler; + +public class ServerInitHandler implements IServerInitDispatcher +{ + private static final ServerInitHandler INSTANCE = new ServerInitHandler(); + public static IServerInitDispatcher getInstance() { return INSTANCE; } + private final List handlers = new ArrayList<>(); + + @Override + public void registerServerInitHandler(IServerInitHandler handler) + { + if (this.handlers.contains(handler) == false) + { + this.handlers.add(handler); + } + } + + @ApiStatus.Internal + public void onServerInit() + { + if (this.handlers.isEmpty() == false) + { + for (IServerInitHandler handler : this.handlers) + { + handler.onServerInit(); + } + } + } +} diff --git a/src/main/java/fi/dy/masa/servux/event/IPlayerManager.java b/src/main/java/fi/dy/masa/servux/interfaces/IPlayerManager.java similarity index 63% rename from src/main/java/fi/dy/masa/servux/event/IPlayerManager.java rename to src/main/java/fi/dy/masa/servux/interfaces/IPlayerManager.java index 0a40ccd..0a7d17b 100644 --- a/src/main/java/fi/dy/masa/servux/event/IPlayerManager.java +++ b/src/main/java/fi/dy/masa/servux/interfaces/IPlayerManager.java @@ -1,6 +1,4 @@ -package fi.dy.masa.servux.event; - -import fi.dy.masa.servux.interfaces.IPlayerListener; +package fi.dy.masa.servux.interfaces; public interface IPlayerManager { diff --git a/src/main/java/fi/dy/masa/servux/interfaces/IServerCommand.java b/src/main/java/fi/dy/masa/servux/interfaces/IServerCommand.java new file mode 100644 index 0000000..3c7386d --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/interfaces/IServerCommand.java @@ -0,0 +1,16 @@ +package fi.dy.masa.servux.interfaces; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; + +public interface IServerCommand +{ + /** + * Register a Server Side command + */ + void register(CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess, + CommandManager.RegistrationEnvironment environment); +} diff --git a/src/main/java/fi/dy/masa/servux/interfaces/IServerInitDispatcher.java b/src/main/java/fi/dy/masa/servux/interfaces/IServerInitDispatcher.java new file mode 100644 index 0000000..220449e --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/interfaces/IServerInitDispatcher.java @@ -0,0 +1,6 @@ +package fi.dy.masa.servux.interfaces; + +public interface IServerInitDispatcher +{ + void registerServerInitHandler(IServerInitHandler handler); +} diff --git a/src/main/java/fi/dy/masa/servux/interfaces/IServerInitHandler.java b/src/main/java/fi/dy/masa/servux/interfaces/IServerInitHandler.java new file mode 100644 index 0000000..49bc2d0 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/interfaces/IServerInitHandler.java @@ -0,0 +1,6 @@ +package fi.dy.masa.servux.interfaces; + +public interface IServerInitHandler +{ + void onServerInit(); +} diff --git a/src/main/java/fi/dy/masa/servux/event/IServerManager.java b/src/main/java/fi/dy/masa/servux/interfaces/IServerManager.java similarity index 63% rename from src/main/java/fi/dy/masa/servux/event/IServerManager.java rename to src/main/java/fi/dy/masa/servux/interfaces/IServerManager.java index a900376..d99160a 100644 --- a/src/main/java/fi/dy/masa/servux/event/IServerManager.java +++ b/src/main/java/fi/dy/masa/servux/interfaces/IServerManager.java @@ -1,6 +1,4 @@ -package fi.dy.masa.servux.event; - -import fi.dy.masa.servux.interfaces.IServerListener; +package fi.dy.masa.servux.interfaces; public interface IServerManager { diff --git a/src/main/java/fi/dy/masa/servux/mixin/MixinCommandManager.java b/src/main/java/fi/dy/masa/servux/mixin/MixinCommandManager.java index 0ac80dd..b6acb0f 100644 --- a/src/main/java/fi/dy/masa/servux/mixin/MixinCommandManager.java +++ b/src/main/java/fi/dy/masa/servux/mixin/MixinCommandManager.java @@ -23,6 +23,6 @@ public class MixinCommandManager private void servux_injectCommands(CommandManager.RegistrationEnvironment environment, CommandRegistryAccess registryAccess, CallbackInfo ci) { - CommandProvider.getInstance().registerCommands(this.dispatcher, registryAccess, environment); + ((CommandProvider) CommandProvider.getInstance()).registerCommands(this.dispatcher, registryAccess, environment); } } diff --git a/src/main/java/fi/dy/masa/servux/mixin/MixinMinecraftDedicatedServer.java b/src/main/java/fi/dy/masa/servux/mixin/MixinMinecraftDedicatedServer.java new file mode 100644 index 0000000..cc25e60 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/mixin/MixinMinecraftDedicatedServer.java @@ -0,0 +1,27 @@ +package fi.dy.masa.servux.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.mojang.datafixers.DataFixer; +import net.minecraft.resource.ResourcePackManager; +import net.minecraft.server.SaveLoader; +import net.minecraft.server.WorldGenerationProgressListenerFactory; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +import net.minecraft.server.dedicated.ServerPropertiesLoader; +import net.minecraft.util.ApiServices; +import net.minecraft.world.level.storage.LevelStorage; +import fi.dy.masa.servux.event.ServerInitHandler; + +@Mixin(MinecraftDedicatedServer.class) +public class MixinMinecraftDedicatedServer +{ + @Inject(method = "", at = @At("TAIL")) + private void servux_DedicatedServerInit(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager, + SaveLoader saveLoader, ServerPropertiesLoader propertiesLoader, DataFixer dataFixer, + ApiServices apiServices, WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) + { + ((ServerInitHandler) ServerInitHandler.getInstance()).onServerInit(); + } +} diff --git a/src/main/java/fi/dy/masa/servux/servux/ServerListener.java b/src/main/java/fi/dy/masa/servux/servux/ServerListener.java index a9b4d4b..1ba6eb1 100644 --- a/src/main/java/fi/dy/masa/servux/servux/ServerListener.java +++ b/src/main/java/fi/dy/masa/servux/servux/ServerListener.java @@ -3,7 +3,9 @@ import net.minecraft.resource.ResourceManager; import net.minecraft.server.MinecraftServer; import fi.dy.masa.servux.dataproviders.DataProviderManager; +import fi.dy.masa.servux.dataproviders.ServuxConfigProvider; import fi.dy.masa.servux.interfaces.IServerListener; +import fi.dy.masa.servux.util.i18nLang; public class ServerListener implements IServerListener { @@ -29,6 +31,8 @@ public void onServerResourceReloadPre(MinecraftServer server, ResourceManager re public void onServerResourceReloadPost(MinecraftServer server, ResourceManager resourceManager, boolean success) { DataProviderManager.INSTANCE.writeToConfig(); + + i18nLang.tryLoadLanguage(ServuxConfigProvider.INSTANCE.getDefaultLanguage()); } @Override diff --git a/src/main/java/fi/dy/masa/servux/servux/ServuxInitHandler.java b/src/main/java/fi/dy/masa/servux/servux/ServuxInitHandler.java new file mode 100644 index 0000000..b9994ab --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/servux/ServuxInitHandler.java @@ -0,0 +1,22 @@ +package fi.dy.masa.servux.servux; + +import fi.dy.masa.servux.dataproviders.*; +import fi.dy.masa.servux.event.PlayerHandler; +import fi.dy.masa.servux.event.ServerHandler; +import fi.dy.masa.servux.interfaces.IServerInitHandler; + +public class ServuxInitHandler implements IServerInitHandler +{ + @Override + public void onServerInit() + { + DataProviderManager.INSTANCE.registerDataProvider(ServuxConfigProvider.INSTANCE); + DataProviderManager.INSTANCE.registerDataProvider(LitematicsDataProvider.INSTANCE); + DataProviderManager.INSTANCE.registerDataProvider(EntitiesDataProvider.INSTANCE); + DataProviderManager.INSTANCE.registerDataProvider(StructureDataProvider.INSTANCE); + //DataProviderManager.INSTANCE.registerDataProvider(TweaksDataProvider.INSTANCE); + + ServerHandler.getInstance().registerServerHandler(new ServerListener()); + PlayerHandler.getInstance().registerPlayerHandler(new PlayerListener()); + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/AbstractServuxSetting.java b/src/main/java/fi/dy/masa/servux/settings/AbstractServuxSetting.java new file mode 100644 index 0000000..7dd1525 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/AbstractServuxSetting.java @@ -0,0 +1,124 @@ +package fi.dy.masa.servux.settings; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import fi.dy.masa.servux.util.i18nLang; +import net.minecraft.text.Text; + +import java.util.List; +import java.util.Objects; + +public abstract class AbstractServuxSetting implements IServuxSetting +{ + private final String name; + private final Text prettyName; + private final Text comment; + private final T defaultValue; + private final List examples; + private final IDataProvider dataProvider; + + public AbstractServuxSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, T defaultValue, List examples) + { + Objects.requireNonNull(name); + this.name = name; + this.prettyName = prettyName; + this.comment = comment; + this.defaultValue = defaultValue; + this.value = defaultValue; + this.examples = examples; + this.dataProvider = dataProvider; + } + + public AbstractServuxSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, T defaultValue) + { + this(dataProvider, name, prettyName, comment, defaultValue, null); + } + + private T value; + + @Override + public T getDefaultValue() + { + return defaultValue; + } + + @Override + public T getValue() + { + return value; + } + + @Override + /** + * the value field should not be modified directly, please invoke this method. + * override this value to handle all the value changes, even caused by reading config. + */ + public void setValueNoCallback(T value) + { + this.value = value; + } + + @Override + public void setValue(T value) throws CommandSyntaxException + { + var oldValue = this.getValue(); + setValueNoCallback(value); + onValueChanged(oldValue, value); + } + + @Override + public IDataProvider dataProvider() + { + return dataProvider; + } + + protected void onValueChanged(T oldValue, T value) + { + + } + + @Override + public void setValueFromString(String value) throws CommandSyntaxException + { + if (this.validateString(value)) + { + setValue(this.valueFromString(value)); + } + } + + @Override + public String name() + { + return name; + } + + @Override + public Text prettyName() + { + if (prettyName == null) + { + return i18nLang.getInstance().translate("servux.config."+dataProvider.getName()+"."+name+".name"); + } + return prettyName; + } + + @Override + public Text comment() + { + if (comment == null) + { + return i18nLang.getInstance().translate("servux.config."+dataProvider.getName()+"."+name+".comment"); + } + return comment; + } + + @Override + public List examples() + { + if (examples == null) + { + return List.of(); + } + return examples; + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/IServuxSetting.java b/src/main/java/fi/dy/masa/servux/settings/IServuxSetting.java new file mode 100644 index 0000000..e9bccbb --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/IServuxSetting.java @@ -0,0 +1,47 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.JsonElement; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.List; + +public interface IServuxSetting +{ + String name(); + Text prettyName(); + Text comment(); + List examples(); + IDataProvider dataProvider(); + + T getDefaultValue(); + T getValue(); + void setValueNoCallback(T value); + void setValue(T value) throws CommandSyntaxException; + + /** + * Set the value from a string representation, this is used when setting the value from commands + * @throws CommandSyntaxException if the value is invalid + */ + void setValueFromString(String value) throws CommandSyntaxException; + boolean validateString(String value); + String valueToString(Object value); + T valueFromString(String value); + void readFromJson(JsonElement element); + JsonElement writeToJson(); + + default Text shortDisplayName() { + return prettyName().copy().styled(style -> + style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, comment().copy() + .append(Text.literal("\n(%s)".formatted(qualifiedName())).formatted(Formatting.DARK_GRAY)))) + .withColor(Formatting.YELLOW) + ); + } + + default String qualifiedName() { + return dataProvider().getName() + ":" + name(); + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/ServuxBoolSetting.java b/src/main/java/fi/dy/masa/servux/settings/ServuxBoolSetting.java new file mode 100644 index 0000000..de201ff --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/ServuxBoolSetting.java @@ -0,0 +1,59 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.Text; + +import java.util.List; + +public class ServuxBoolSetting extends AbstractServuxSetting +{ + public ServuxBoolSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, boolean defaultValue) + { + super(dataProvider, name, prettyName, comment, defaultValue, List.of("true", "false")); + } + + public ServuxBoolSetting(IDataProvider dataProvider, String name, boolean defaultValue) + { + super(dataProvider, name, null, null, defaultValue, List.of("true", "false")); + } + + @Override + public boolean validateString(String value) + { + return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"); + } + + @Override + public String valueToString(Object value) + { + return ((Boolean) value).toString(); + } + + @Override + public Boolean valueFromString(String value) + { + return Boolean.parseBoolean(value); + } + + @Override + public void readFromJson(JsonElement element) + { + if (element.isJsonPrimitive()) + { + var value = element.getAsJsonPrimitive(); + + if (value.isBoolean()) + { + this.setValueNoCallback(value.getAsBoolean()); + } + } + } + + @Override + public JsonElement writeToJson() + { + return new JsonPrimitive(this.getValue()); + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/ServuxIntSetting.java b/src/main/java/fi/dy/masa/servux/settings/ServuxIntSetting.java new file mode 100644 index 0000000..4c4cd7a --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/ServuxIntSetting.java @@ -0,0 +1,80 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.Text; + +public class ServuxIntSetting extends AbstractServuxSetting +{ + private final int maxValue; + private final int minValue; + + public ServuxIntSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, int defaultValue, int maxValue, int minValue) + { + super(dataProvider ,name, prettyName, comment, defaultValue); + this.maxValue = maxValue; + this.minValue = minValue; + } + + public ServuxIntSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, int defaultValue) + { + this(dataProvider, name, prettyName, comment, defaultValue, Integer.MAX_VALUE, Integer.MIN_VALUE); + } + + public ServuxIntSetting(IDataProvider dataProvider, String name, int defaultValue, int maxValue, int minValue) + { + this(dataProvider, name, null, null, defaultValue, maxValue, minValue); + } + + public ServuxIntSetting(IDataProvider dataProvider, String name, int defaultValue) + { + this(dataProvider, name, null, null, defaultValue, Integer.MAX_VALUE, Integer.MIN_VALUE); + } + + @Override + public boolean validateString(String value) + { + try + { + int val = Integer.parseInt(value); + return val >= this.minValue && val <= this.maxValue; + } + catch (NumberFormatException e) + { + return false; + } + } + + @Override + public String valueToString(Object value) + { + return ((Integer) value).toString(); + } + + @Override + public Integer valueFromString(String value) + { + return Integer.parseInt(value); + } + + @Override + public void readFromJson(JsonElement element) + { + if (element.isJsonPrimitive()) + { + var value = element.getAsJsonPrimitive(); + + if (value.isNumber()) + { + this.setValueNoCallback(value.getAsInt()); + } + } + } + + @Override + public JsonElement writeToJson() + { + return new JsonPrimitive(this.getValue()); + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/ServuxListSetting.java b/src/main/java/fi/dy/masa/servux/settings/ServuxListSetting.java new file mode 100644 index 0000000..9ac5f0e --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/ServuxListSetting.java @@ -0,0 +1,85 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.Text; + +import java.util.List; +import java.util.stream.Collectors; + +public abstract class ServuxListSetting extends AbstractServuxSetting> +{ + private static final Gson GSON = new Gson(); + + public ServuxListSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, List defaultValue, List examples, String separatorRegex) + { + this(dataProvider, name, prettyName, comment, defaultValue, examples); + } + + public ServuxListSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, List defaultValue, List examples) + { + super(dataProvider, name, prettyName, comment, defaultValue, examples); + } + + @Override + public boolean validateString(String value) + { + JsonArray array = GSON.fromJson(value, JsonArray.class); + for (JsonElement element : array) + { + if (!this.validateJsonForElement(element)) + { + return false; + } + } + return true; + } + + public abstract boolean validateJsonForElement(JsonElement value); + + @SuppressWarnings("unchecked") + @Override + public String valueToString(Object value) + { + JsonArray array = new JsonArray(); + for (T ele : (List) value) + { + array.add(this.writeElementToJson(ele)); + } + return GSON.toJson(array); + } + + @Override + public List valueFromString(String value) + { + return GSON.fromJson(value, JsonArray.class).asList().stream().map(this::readElementFromJson).toList(); + } + + @Override + public void readFromJson(JsonElement element) + { + if (element.isJsonArray()) + { + var array = element.getAsJsonArray(); + var list = array.asList().stream().map(this::readElementFromJson).collect(Collectors.toList()); + this.setValueNoCallback(list); + } + } + + public abstract T readElementFromJson(JsonElement element); + + @Override + public JsonElement writeToJson() + { + JsonArray array = new JsonArray(); + for (T value : this.getValue()) + { + array.add(this.writeElementToJson(value)); + } + return array; + } + + public abstract JsonElement writeElementToJson(T value); +} diff --git a/src/main/java/fi/dy/masa/servux/settings/ServuxStringListSetting.java b/src/main/java/fi/dy/masa/servux/settings/ServuxStringListSetting.java new file mode 100644 index 0000000..afce8d0 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/ServuxStringListSetting.java @@ -0,0 +1,44 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.Text; + +import java.util.List; + +public class ServuxStringListSetting extends ServuxListSetting +{ + public ServuxStringListSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, List defaultValue, List examples) + { + super(dataProvider, name, prettyName, comment, defaultValue, examples); + } + + public ServuxStringListSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, List defaultValue) + { + super(dataProvider, name, prettyName, comment, defaultValue, List.of()); + } + + public ServuxStringListSetting(IDataProvider dataProvider, String name, List defaultValue) + { + super(dataProvider, name, null, null, defaultValue, List.of()); + } + + @Override + public boolean validateJsonForElement(JsonElement value) + { + return value instanceof JsonPrimitive primitive && primitive.isString(); + } + + @Override + public String readElementFromJson(JsonElement element) + { + return element.getAsString(); + } + + @Override + public JsonElement writeElementToJson(String value) + { + return new JsonPrimitive(value); + } +} diff --git a/src/main/java/fi/dy/masa/servux/settings/ServuxStringSetting.java b/src/main/java/fi/dy/masa/servux/settings/ServuxStringSetting.java new file mode 100644 index 0000000..ebd7e12 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/settings/ServuxStringSetting.java @@ -0,0 +1,56 @@ +package fi.dy.masa.servux.settings; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import fi.dy.masa.servux.dataproviders.IDataProvider; +import net.minecraft.text.Text; + +import java.util.List; + +public class ServuxStringSetting extends AbstractServuxSetting +{ + private final boolean strict; + public ServuxStringSetting(IDataProvider dataProvider, String name, String defaultValue, List examples, boolean strict) + { + this(dataProvider, name, null, null, defaultValue, examples, strict); + } + + public ServuxStringSetting(IDataProvider dataProvider, String name, Text prettyName, Text comment, String defaultValue, List examples, boolean strict) + { + super(dataProvider, name, prettyName, comment, defaultValue, examples); + this.strict = strict; + } + + @Override + public boolean validateString(String value) + { + return strict ? this.examples().contains(value) : true; + } + + @Override + public String valueToString(Object value) + { + return (String) value; + } + + @Override + public String valueFromString(String value) + { + return value; + } + + @Override + public void readFromJson(JsonElement element) + { + if (element instanceof JsonPrimitive primitive) + { + this.setValueNoCallback(primitive.getAsString()); + } + } + + @Override + public JsonElement writeToJson() + { + return new JsonPrimitive(this.getValue()); + } +} diff --git a/src/main/java/fi/dy/masa/servux/util/StringUtils.java b/src/main/java/fi/dy/masa/servux/util/StringUtils.java index 59d1585..a72abb5 100644 --- a/src/main/java/fi/dy/masa/servux/util/StringUtils.java +++ b/src/main/java/fi/dy/masa/servux/util/StringUtils.java @@ -1,5 +1,10 @@ package fi.dy.masa.servux.util; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.minecraft.text.MutableText; +import net.minecraft.util.Identifier; + public class StringUtils { public static String getModVersionString(String modId) @@ -14,4 +19,24 @@ public static String getModVersionString(String modId) return "?"; } + + public static String removeDefaultMinecraftNamespace(Identifier settingId) + { + return settingId.getNamespace().equals("minecraft") ? settingId.getPath() : settingId.toString(); + } + + /** + * Can replace I18n + * @param translationKey (key) + * @param args (...args) + */ + public static MutableText translate(String translationKey, Object... args) + { + return i18nLang.getInstance().translate(translationKey, args); + } + + public static CommandSyntaxException translateError(String translationKey, Object... args) + { + return new SimpleCommandExceptionType(translate(translationKey, args)).create(); + } } diff --git a/src/main/java/fi/dy/masa/servux/util/i18nLang.java b/src/main/java/fi/dy/masa/servux/util/i18nLang.java new file mode 100644 index 0000000..48f2df2 --- /dev/null +++ b/src/main/java/fi/dy/masa/servux/util/i18nLang.java @@ -0,0 +1,140 @@ +package fi.dy.masa.servux.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.function.BiConsumer; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.text.*; +import net.minecraft.util.Formatting; +import fi.dy.masa.servux.Reference; +import fi.dy.masa.servux.Servux; + +import javax.annotation.Nullable; + +public class i18nLang +{ + private static final Gson GSON = new Gson(); + public static final String DEFAULT_LANG = "en_us"; + public static final String DEFAULT_PATH = "/assets/"+Reference.MOD_ID+"/lang/"; + private static volatile i18nLang instance = null; + static + { + tryLoadLanguage(DEFAULT_LANG); + } + private final String languageCode; + private final Map map; + + public i18nLang(String languageCode, Map map) + { + this.languageCode = languageCode; + this.map = map; + } + + public static i18nLang create(String languageCode, String path) throws IOException + { + ImmutableMap.Builder builder = ImmutableMap.builder(); + BiConsumer biConsumer = builder::put; + load(biConsumer, path); + final Map map = builder.build(); + + return new i18nLang(languageCode, map); + } + + public static i18nLang create(String languageCode) throws IOException + { + return create(languageCode, DEFAULT_PATH + languageCode + ".json"); + } + + public static void load(BiConsumer entryConsumer, String path) throws IOException + { + InputStream inputStream = i18nLang.class.getResourceAsStream(path); + + try + { + if (inputStream != null) + { + JsonObject jsonObject = GSON.fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonObject.class); + + for (Map.Entry entry : jsonObject.entrySet()) + { + entryConsumer.accept(entry.getKey(), entry.getValue().getAsString()); + } + } + else + { + throw new IOException("Couldn't find the file: " + path); + } + } + catch (Throwable var6) + { + if (inputStream != null) + { + try + { + inputStream.close(); + } + catch (Throwable var5) + { + var6.addSuppressed(var5); + } + } + + throw var6; + } + + inputStream.close(); + } + + public static i18nLang getInstance() + { + return instance; + } + + public static void setInstance(i18nLang language) + { + instance = language; + } + + public static boolean tryLoadLanguage(String langCode) + { + try + { + instance = create(langCode); + return true; + } + catch (Exception e) + { + Servux.logger.error("Failed to load language file for '{}'", langCode, e); + return false; + } + } + + public @Nullable String get(String key) + { + return map.get(key); + } + + public MutableText translate(String key, Object... args) + { + if (hasTranslation(key)) + { + return Text.translatableWithFallback(key, get(key), args); + } + else + { + return Text.literal(key).styled((style) -> style.withColor(Formatting.RED).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.of("Missing translation: "+key)))); + } + } + + public boolean hasTranslation(String key) + { + return map.containsKey(key); + } +} diff --git a/src/main/resources/assets/servux/lang/en_us.json b/src/main/resources/assets/servux/lang/en_us.json new file mode 100644 index 0000000..1c019f2 --- /dev/null +++ b/src/main/resources/assets/servux/lang/en_us.json @@ -0,0 +1,64 @@ +{ + "servux.command.error.unknown_setting": "Unknown setting", + "servux.command.error.unknown_data_provider": "Unknown data provider", + "servux.command.error.no_settings": "No settings found!", + "servux.command.error.invalid_value": "Invalid value", + "servux.command.info.current_value": "Current value: %s", + "servux.command.info.value": "Value: %s", + "servux.command.search.results": "Found %s settings for '%s'", + "servux.command.search.none": "No settings found for '%s'", + "servux.command.suffix.default_value": "(default value)", + "servux.command.suffix.modified": "(modified)", + "servux.command.info.reset": "Reset", + "servux.command.info.click_to_reset_to": "Click to reset to default value: %s", + "servux.command.info.default_value": "Default: %s", + "servux.command.config.set_value": "Set %s to %s", + "servux.command.config.reloaded": "Reloaded config!", + "servux.command.config.saved": "Saved config!", + "servux.command.info.examples": "Examples: ", + "servux.command.info.click_to_set": "Click to set value: %s", + "servux.command.info.click_to_see_more": "Click to see more", + "servux.command.info.click_to_copy": "Click to copy", + "servux.command.config.invalid_language": "Invalid language: %s", + "servux.command.config.list.data_provider": "Data provider %s has following settings: ", + + "servux.config.servux_main.default_permission_level.name": "Permission Level", + "servux.config.servux_main.default_permission_level.comment": "The permission level required to access the data provider", + "servux.config.servux_main.default_language.name": "Default Language", + "servux.config.servux_main.default_language.comment": "The default language for servux", + "servux.config.servux_main.permission_level_easy_place.name": "Easy Place Permission Level", + "servux.config.servux_main.permission_level_easy_place.comment": "The permission level required for the Easy Place operation", + "servux.config.servux_main.permission_level_admin.name": "Admin Permission Level", + "servux.config.servux_main.permission_level_admin.comment": "The permission level required for the Admin operation (not used)", + "servux.config.servux_main.permission_level.name": "Permission Level", + "servux.config.servux_main.permission_level.comment": "The permission level required for the data provider (not used)", + + "servux.config.litematic_data.permission_level.name": "Permission Level", + "servux.config.litematic_data.permission_level.comment": "The permission level required for the Litematics data provider", + "servux.config.litematic_data.permission_level_paste.name": "Paste Permission Level", + "servux.config.litematic_data.permission_level_paste.comment": "The permission level required for the Litematics paste operation", + + "servux.config.entity_data.permission_level.name": "Permission Level", + "servux.config.entity_data.permission_level.comment": "The permission level required for the Entity data provider, will send entity data to clients with this permission level", + + "servux.config.structure_bounding_boxes.permission_level.name": "Permission Level", + "servux.config.structure_bounding_boxes.permission_level.comment": "The permission level required for the Structure Bounding Boxes data provider", + "servux.config.structure_bounding_boxes.structures_blacklist.name": "Structures Blacklist", + "servux.config.structure_bounding_boxes.structures_blacklist.comment": "A list of structures to exclude from the Structure Bounding Boxes data provider", + "servux.config.structure_bounding_boxes.structures_blacklist_enabled.name": "Structures Blacklist Enabled", + "servux.config.structure_bounding_boxes.structures_blacklist_enabled.comment": "Whether to enable the structures blacklist", + "servux.config.structure_bounding_boxes.structures_whitelist.name": "Structures Whitelist", + "servux.config.structure_bounding_boxes.structures_whitelist.comment": "A list of structures to include in the Structure Bounding Boxes data provider", + "servux.config.structure_bounding_boxes.structures_whitelist_enabled.name": "Structures Whitelist Enabled", + "servux.config.structure_bounding_boxes.structures_whitelist_enabled.comment": "Whether to enable the structures whitelist", + "servux.config.structure_bounding_boxes.timeout.name": "Timeout", + "servux.config.structure_bounding_boxes.timeout.comment": "The timeout in ticks for the Structure Bounding Boxes data provider, will resend the bounding box data after this many ticks", + "servux.config.structure_bounding_boxes.update_interval.name": "Update Interval", + "servux.config.structure_bounding_boxes.update_interval.comment": "The update interval in ticks for the Structure Bounding Boxes data provider, will check for new chunks to send bounding box data for this often", + + "servux.litematics.error.insufficent_for_paste": "§cInsufficient Permissions for the Litematic paste operation.§r", + "servux.litematics.error.creative_required": "§cCreative Mode is required for the Litematic paste operation.§r", + "servux.litematics.success.pasted": "Pasted §b%s§r to world §d%s§r in §a%s§rms.", + + "servux.suffix.data_provider": " (Data Provider)" +} diff --git a/src/main/resources/assets/servux/lang/zh_cn.json b/src/main/resources/assets/servux/lang/zh_cn.json new file mode 100644 index 0000000..ed0b0cf --- /dev/null +++ b/src/main/resources/assets/servux/lang/zh_cn.json @@ -0,0 +1,64 @@ +{ + "servux.command.error.unknown_setting": "未知设置", + "servux.command.error.unknown_data_provider": "未知数据源", + "servux.command.error.no_settings": "未找到设置!", + "servux.command.error.invalid_value": "无效的值", + "servux.command.info.value": "当前值:%s", + "servux.command.suffix.default_value": "(默认值)", + "servux.command.suffix.modified": "(已修改)", + "servux.command.info.reset": "重置", + "servux.command.info.click_to_reset_to": "点击重置为默认值:%s", + "servux.command.info.current_value": "当前值:%s", + "servux.command.info.default_value": "默认值:%s", + "servux.command.config.set_value": "成功将 %s 设置为 %s", + "servux.command.config.reloaded": "配置已重新加载!", + "servux.command.config.saved": "配置已保存!", + "servux.command.info.examples": "示例:", + "servux.command.info.click_to_set": "点击设置值:%s", + "servux.command.info.click_to_see_more": "点击查看更多", + "servux.command.info.click_to_copy": "点击复制", + "servux.command.config.invalid_language": "无效的语言:%s", + "servux.command.config.list.data_provider": "数据源 %s 具有以下设置:", + + "servux.config.servux_main.default_permission_level.name": "权限级别", + "servux.config.servux_main.default_permission_level.comment": "访问数据源所需的权限级别", + "servux.config.servux_main.default_language.name": "默认语言", + "servux.config.servux_main.default_language.comment": "Servux 的默认语言", + "servux.config.servux_main.permission_level_easy_place.name": "轻松放置权限级别", + "servux.config.servux_main.permission_level_easy_place.comment": "执行投影轻松放置操作所需的权限级别", + "servux.config.servux_main.permission_level_admin.name": "管理员权限级别", + "servux.config.servux_main.permission_level_admin.comment": "执行管理员操作所需的权限级别(未使用)", + "servux.config.servux_main.permission_level.name": "权限级别", + "servux.config.servux_main.permission_level.comment": "数据源所需的权限级别(未使用)", + + "servux.config.litematic_data.permission_level.name": "权限级别", + "servux.config.litematic_data.permission_level.comment": "Litematics 数据源所需的权限级别", + "servux.config.litematic_data.permission_level_paste.name": "粘贴权限级别", + "servux.config.litematic_data.permission_level_paste.comment": "Litematics 粘贴操作所需的权限级别", + + "servux.config.entity_data.permission_level.name": "权限级别", + "servux.config.entity_data.permission_level.comment": "实体数据源所需的权限级别,将向具有该权限级别的玩家端发送实体数据", + + "servux.config.structure_bounding_boxes.permission_level.name": "权限级别", + "servux.config.structure_bounding_boxes.permission_level.comment": "结构边界框数据源所需的权限级别", + "servux.config.structure_bounding_boxes.structures_blacklist.name": "结构黑名单", + "servux.config.structure_bounding_boxes.structures_blacklist.comment": "要从结构边界框数据源中排除的结构列表", + "servux.config.structure_bounding_boxes.structures_blacklist_enabled.name": "结构黑名单启用", + "servux.config.structure_bounding_boxes.structures_blacklist_enabled.comment": "是否启用结构黑名单", + "servux.config.structure_bounding_boxes.structures_whitelist.name": "结构白名单", + "servux.config.structure_bounding_boxes.structures_whitelist.comment": "要在结构边界框数据源中发送的结构列表", + "servux.config.structure_bounding_boxes.structures_whitelist_enabled.name": "结构白名单启用", + "servux.config.structure_bounding_boxes.structures_whitelist_enabled.comment": "是否启用结构白名单", + "servux.config.structure_bounding_boxes.timeout.name": "超时", + "servux.config.structure_bounding_boxes.timeout.comment": "结构边界框数据源的超时时间(以刻为单位),将在此时间后重新发送边界框数据", + "servux.config.structure_bounding_boxes.update_interval.name": "更新间隔", + "servux.config.structure_bounding_boxes.update_interval.comment": "结构边界框数据源的更新间隔(以刻为单位),将以此频率检查新区块以发送边界框数据", + + "servux.litematics.error.insufficent_for_paste": "§cLitematic 粘贴操作权限不足.§r", + "servux.litematics.error.creative_required": "§cLitematic 粘贴操作需要创造模式.§r", + "servux.litematics.success.pasted": "已将 §b%s§r 粘贴到世界 §d%s§r 中,耗时 §a%s§r 毫秒。", + + "servux.suffix.data_provider": "(数据源)", + "servux.command.search.results": "找到 %s 个关于 '%s' 的设置", + "servux.command.search.none": "未找到关于 '%s' 的设置" +} diff --git a/src/main/resources/mixins.servux.json b/src/main/resources/mixins.servux.json index 5bb3fdc..d1312dc 100644 --- a/src/main/resources/mixins.servux.json +++ b/src/main/resources/mixins.servux.json @@ -6,6 +6,7 @@ "mixins": [ "MixinBlockItem", "MixinCommandManager", + "MixinMinecraftDedicatedServer", "MixinMinecraftServer", "MixinPlayerManager", "MixinServerChunkLoadingManager",