From ae09fc6b6d418f195db0d567fe228157c16496cc Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 28 Jul 2019 21:09:15 -0400 Subject: [PATCH] Register a config command. Currently one subcommand: showfile, which when passed a modid and a type (CLIENT, COMMON or SERVER) will display a link in the caller's chat to open that file in the OS file viewer of choice. Signed-off-by: cpw --- .../net/minecraftforge/common/ForgeMod.java | 2 + .../fml/config/ConfigTracker.java | 9 ++++ .../server/command/ConfigCommand.java | 54 +++++++++++++++++++ .../server/command/EnumArgument.java | 40 ++++++++++++++ .../server/command/ModIdArgument.java | 39 ++++++++++++++ .../resources/assets/forge/lang/en_us.json | 2 + 6 files changed, 146 insertions(+) create mode 100644 src/main/java/net/minecraftforge/server/command/ConfigCommand.java create mode 100644 src/main/java/net/minecraftforge/server/command/EnumArgument.java create mode 100644 src/main/java/net/minecraftforge/server/command/ModIdArgument.java diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index 6a435e491..73bbaf876 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -29,6 +29,7 @@ import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.server.command.ConfigCommand; import net.minecraftforge.server.command.ForgeCommand; import net.minecraftforge.versions.forge.ForgeVersion; import net.minecraftforge.versions.mcp.MCPVersion; @@ -181,6 +182,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook public void serverStarting(FMLServerStartingEvent evt) { new ForgeCommand(evt.getCommandDispatcher()); + ConfigCommand.register(evt.getCommandDispatcher()); } public void serverStopping(FMLServerStoppingEvent evt) diff --git a/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java b/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java index eb55acceb..baf38270e 100644 --- a/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java +++ b/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java @@ -22,6 +22,7 @@ package net.minecraftforge.fml.config; import com.electronwill.nightconfig.core.CommentedConfig; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.toml.TomlFormat; +import it.unimi.dsi.fastutil.bytes.Byte2ByteOpenCustomHashMap; import net.minecraft.client.Minecraft; import net.minecraftforge.fml.network.FMLHandshakeMessages; import net.minecraftforge.fml.network.NetworkEvent; @@ -54,10 +55,12 @@ public class ConfigTracker { public static final ConfigTracker INSTANCE = new ConfigTracker(); private final ConcurrentHashMap fileMap; private final EnumMap> configSets; + private ConcurrentHashMap> configsByMod; private ConfigTracker() { this.fileMap = new ConcurrentHashMap<>(); this.configSets = new EnumMap<>(ModConfig.Type.class); + this.configsByMod = new ConcurrentHashMap<>(); this.configSets.put(ModConfig.Type.CLIENT, Collections.synchronizedSet(new LinkedHashSet<>())); this.configSets.put(ModConfig.Type.COMMON, Collections.synchronizedSet(new LinkedHashSet<>())); // this.configSets.put(ModConfig.Type.PLAYER, new ConcurrentSkipListSet<>()); @@ -71,6 +74,7 @@ public class ConfigTracker { } this.fileMap.put(config.getFileName(), config); this.configSets.get(config.getType()).add(config); + this.configsByMod.computeIfAbsent(config.getModId(), (k)->new EnumMap<>(ModConfig.Type.class)).put(config.getType(), config); LOGGER.debug(CONFIG, "Config file {} for {} tracking", config.getFileName(), config.getModId()); } @@ -115,4 +119,9 @@ public class ConfigTracker { modConfig.fireEvent(new ModConfig.Loading(modConfig)); }); } + + public String getConfigFileName(String modId, ModConfig.Type type) { + return Optional.ofNullable(configsByMod.getOrDefault(modId, Collections.emptyMap()).getOrDefault(type, null)). + map(ModConfig::getFullPath).map(Object::toString).orElse(null); + } } diff --git a/src/main/java/net/minecraftforge/server/command/ConfigCommand.java b/src/main/java/net/minecraftforge/server/command/ConfigCommand.java new file mode 100644 index 000000000..8706935c5 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/command/ConfigCommand.java @@ -0,0 +1,54 @@ +package net.minecraftforge.server.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraftforge.fml.config.ConfigTracker; +import net.minecraftforge.fml.config.ModConfig; + +import java.io.File; + +public class ConfigCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("config"). + then(ShowFile.register()) + ); + } + + public static class ShowFile { + static ArgumentBuilder register() { + return Commands.literal("showfile"). + requires(cs->cs.hasPermissionLevel(0)). + then(Commands.argument("mod", ModIdArgument.modIdArgument()). + then(Commands.argument("type", EnumArgument.enumArgument(ModConfig.Type.class)). + executes(ShowFile::showFile) + ) + ); + } + + private static int showFile(final CommandContext context) { + final String modId = context.getArgument("mod", String.class); + final ModConfig.Type type = context.getArgument("type", ModConfig.Type.class); + final String configFileName = ConfigTracker.INSTANCE.getConfigFileName(modId, type); + if (configFileName != null) { + File f = new File(configFileName); + context.getSource().sendFeedback(new TranslationTextComponent("commands.config.getwithtype", + modId, type, + new StringTextComponent(f.getName()).applyTextStyle(TextFormatting.UNDERLINE). + applyTextStyle((style) -> style.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, f.getAbsolutePath()))) + ), true); + } else { + context.getSource().sendFeedback(new TranslationTextComponent("commands.config.noconfig", modId, type), + true); + } + return 0; + } + } +} diff --git a/src/main/java/net/minecraftforge/server/command/EnumArgument.java b/src/main/java/net/minecraftforge/server/command/EnumArgument.java new file mode 100644 index 000000000..25cd6a8c4 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/command/EnumArgument.java @@ -0,0 +1,40 @@ +package net.minecraftforge.server.command; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.ISuggestionProvider; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class EnumArgument> implements ArgumentType { + private final Class enumClass; + + public static > EnumArgument enumArgument(Class enumClass) { + return new EnumArgument<>(enumClass); + } + private EnumArgument(final Class enumClass) { + this.enumClass = enumClass; + } + + @Override + public T parse(final StringReader reader) throws CommandSyntaxException { + return Enum.valueOf(enumClass, reader.readUnquotedString()); + } + + @Override + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + return ISuggestionProvider.suggest(Stream.of(enumClass.getEnumConstants()).map(Object::toString), builder); + } + + @Override + public Collection getExamples() { + return Stream.of(enumClass.getEnumConstants()).map(Object::toString).collect(Collectors.toList()); + } +} diff --git a/src/main/java/net/minecraftforge/server/command/ModIdArgument.java b/src/main/java/net/minecraftforge/server/command/ModIdArgument.java new file mode 100644 index 000000000..0645518d7 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/command/ModIdArgument.java @@ -0,0 +1,39 @@ +package net.minecraftforge.server.command; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.ISuggestionProvider; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.fml.ModList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class ModIdArgument implements ArgumentType { + private static final List EXAMPLES = Arrays.asList("forge", "inventorysorter"); + + public static ModIdArgument modIdArgument() { + return new ModIdArgument(); + } + + @Override + public String parse(final StringReader reader) throws CommandSyntaxException { + return reader.readUnquotedString(); + } + + @Override + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + return ISuggestionProvider.suggest(ModList.get().applyForEachModContainer(ModContainer::getModId), builder); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index 45d218309..40044632d 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -98,6 +98,8 @@ "commands.forge.tracking.timing_entry": "{0} - {1} [{2}, {3}, {4}]: {5}", "commands.forge.tracking.no_data": "No data has been recorded yet.", + "commands.config.getwithtype": "Config for %s of type %s found at %s", + "commands.config.noconfig": "Config for %s of type %s not found", "forge.update.beta.1": "%sWARNING: %sForge Beta", "forge.update.beta.2": "Major issues may arise, verify before reporting.",