Skip to content

Commit

Permalink
feat: servux setting (#9)
Browse files Browse the repository at this point in the history
* 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 <sakura@ryoko.soy>
  • Loading branch information
zly2006 and sakura-ryoko authored Jul 28, 2024
1 parent 97e2e85 commit fc503dc
Show file tree
Hide file tree
Showing 37 changed files with 1,445 additions and 292 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 8 additions & 16 deletions src/main/java/fi/dy/masa/servux/Servux.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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)
Expand Down
30 changes: 24 additions & 6 deletions src/main/java/fi/dy/masa/servux/commands/CommandProvider.java
Original file line number Diff line number Diff line change
@@ -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<IServerCommand> 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<ServerCommandSource> dispatcher,
CommandRegistryAccess registryAccess,
CommandManager.RegistrationEnvironment environment)
{
ServuxCommand.register(dispatcher);
this.commands.forEach((command) -> command.register(dispatcher, registryAccess, environment));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
274 changes: 271 additions & 3 deletions src/main/java/fi/dy/masa/servux/commands/ServuxCommand.java
Original file line number Diff line number Diff line change
@@ -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<ServerCommandSource> dispatcher)
public static final ServuxCommand INSTANCE = new ServuxCommand();

@Override
public void register(CommandDispatcher<ServerCommandSource> dispatcher,
CommandRegistryAccess registryAccess,
CommandManager.RegistrationEnvironment environment)
{
dispatcher.register(CommandManager
.literal(Reference.MOD_ID).requires(Permissions.require(Reference.MOD_ID + ".commands", 4))
Expand All @@ -25,6 +51,248 @@ public static void register(CommandDispatcher<ServerCommandSource> 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<IDataProvider> 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<IServuxSetting<?>> configSearch(CommandContext<ServerCommandSource> 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<ServerCommandSource> ctx, List<IServuxSetting<?>> list)
{
if (list.isEmpty())
{
ctx.getSource().sendFeedback(() -> StringUtils.translate("servux.command.error.no_settings"), false);
return 0;
}

Set<String> appearedNames = new HashSet<>();
Set<String> 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<ServerCommandSource, ?> 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<ServerCommandSource> 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<ServerCommandSource> 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;
}
}
Loading

0 comments on commit fc503dc

Please sign in to comment.