diff --git a/src/main/java/slimeknights/tconstruct/library/modifiers/ModifierHooks.java b/src/main/java/slimeknights/tconstruct/library/modifiers/ModifierHooks.java index 3d479652056..20f10fcab36 100644 --- a/src/main/java/slimeknights/tconstruct/library/modifiers/ModifierHooks.java +++ b/src/main/java/slimeknights/tconstruct/library/modifiers/ModifierHooks.java @@ -4,6 +4,7 @@ import javax.annotation.Nullable; import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -14,6 +15,8 @@ private ModifierHooks() {} /** Map of ID to hook */ private static final Map> HOOKS = new ConcurrentHashMap<>(); + /** Unmodifiable view of the hook map */ + private static final Collection HOOK_IDS = Collections.unmodifiableCollection(HOOKS.keySet()); /* Registry */ @@ -67,4 +70,9 @@ public static ModifierHook register(ResourceLocation name, Class filte public static ModifierHook register(ResourceLocation name, Class filter, T defaultInstance) { return register(name, filter, defaultInstance, null); } + + /** Gets an unmodifiable view of the list of all registered hooks */ + public static Collection listAllIDs() { + return HOOK_IDS; + } } diff --git a/src/main/java/slimeknights/tconstruct/shared/command/TConstructCommand.java b/src/main/java/slimeknights/tconstruct/shared/command/TConstructCommand.java index 485568266ae..f164c928ca2 100644 --- a/src/main/java/slimeknights/tconstruct/shared/command/TConstructCommand.java +++ b/src/main/java/slimeknights/tconstruct/shared/command/TConstructCommand.java @@ -10,9 +10,11 @@ import slimeknights.tconstruct.TConstruct; import slimeknights.tconstruct.shared.command.argument.MaterialArgument; import slimeknights.tconstruct.shared.command.argument.ModifierArgument; +import slimeknights.tconstruct.shared.command.argument.ModifierHookArgument; import slimeknights.tconstruct.shared.command.argument.SlotTypeArgument; import slimeknights.tconstruct.shared.command.argument.ToolStatArgument; import slimeknights.tconstruct.shared.command.subcommand.GeneratePartTexturesCommand; +import slimeknights.tconstruct.shared.command.subcommand.ModifierPriorityCommand; import slimeknights.tconstruct.shared.command.subcommand.ModifierUsageCommand; import slimeknights.tconstruct.shared.command.subcommand.ModifiersCommand; import slimeknights.tconstruct.shared.command.subcommand.SlotsCommand; @@ -28,6 +30,7 @@ public static void init() { ArgumentTypes.register(TConstruct.resourceString("tool_stat"), ToolStatArgument.class, new EmptyArgumentSerializer<>(ToolStatArgument::stat)); ArgumentTypes.register(TConstruct.resourceString("modifier"), ModifierArgument.class, new EmptyArgumentSerializer<>(ModifierArgument::modifier)); ArgumentTypes.register(TConstruct.resourceString("material"), MaterialArgument.class, new EmptyArgumentSerializer<>(MaterialArgument::material)); + ArgumentTypes.register(TConstruct.resourceString("modifier_hook"), ModifierHookArgument.class, new EmptyArgumentSerializer<>(ModifierHookArgument::modifierHook)); // add command listener MinecraftForge.EVENT_BUS.addListener(TConstructCommand::registerCommand); @@ -48,7 +51,10 @@ private static void registerCommand(RegisterCommandsEvent event) { register(builder, "modifiers", ModifiersCommand::register); register(builder, "tool_stats", StatsCommand::register); register(builder, "slots", SlotsCommand::register); - register(builder, "modifier_usage", ModifierUsageCommand::register); + register(builder, "report", b -> { + register(b, "modifier_usage", ModifierUsageCommand::register); + register(b, "modifier_priority", ModifierPriorityCommand::register); + }); register(builder, "generate_part_textures", GeneratePartTexturesCommand::register); // register final command diff --git a/src/main/java/slimeknights/tconstruct/shared/command/argument/ModifierHookArgument.java b/src/main/java/slimeknights/tconstruct/shared/command/argument/ModifierHookArgument.java new file mode 100644 index 00000000000..3b028743e1e --- /dev/null +++ b/src/main/java/slimeknights/tconstruct/shared/command/argument/ModifierHookArgument.java @@ -0,0 +1,52 @@ +package slimeknights.tconstruct.shared.command.argument; + +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.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import lombok.NoArgsConstructor; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.resources.ResourceLocation; +import slimeknights.tconstruct.TConstruct; +import slimeknights.tconstruct.library.modifiers.ModifierHook; +import slimeknights.tconstruct.library.modifiers.ModifierHooks; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +/** Argument type for a modifier */ +@NoArgsConstructor(staticName = "modifierHook") +public class ModifierHookArgument implements ArgumentType> { + private static final Collection EXAMPLES = Arrays.asList("tconstruct:tool_stats", "tconstruct:tooltip"); + private static final DynamicCommandExceptionType HOOK_NOT_FOUND = new DynamicCommandExceptionType(name -> TConstruct.makeTranslation("command", "modifier_hook.not_found", name)); + + @Override + public ModifierHook parse(StringReader reader) throws CommandSyntaxException { + ResourceLocation loc = ResourceLocation.read(reader); + ModifierHook hook = ModifierHooks.getHook(loc); + if (hook == null) { + throw HOOK_NOT_FOUND.create(loc); + } + return hook; + } + + /** Gets a modifier from the command context */ + public static ModifierHook getModifier(CommandContext context, String name) { + return context.getArgument(name, ModifierHook.class); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return SharedSuggestionProvider.suggestResource(ModifierHooks.listAllIDs(), builder); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/slimeknights/tconstruct/shared/command/subcommand/ModifierPriorityCommand.java b/src/main/java/slimeknights/tconstruct/shared/command/subcommand/ModifierPriorityCommand.java new file mode 100644 index 00000000000..702e93d7595 --- /dev/null +++ b/src/main/java/slimeknights/tconstruct/shared/command/subcommand/ModifierPriorityCommand.java @@ -0,0 +1,73 @@ +package slimeknights.tconstruct.shared.command.subcommand; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.util.TablePrinter; +import slimeknights.mantle.command.MantleCommand; +import slimeknights.tconstruct.TConstruct; +import slimeknights.tconstruct.library.modifiers.Modifier; +import slimeknights.tconstruct.library.modifiers.ModifierHook; +import slimeknights.tconstruct.library.modifiers.ModifierManager; +import slimeknights.tconstruct.shared.command.argument.ModifierHookArgument; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** Command to list priorities for all modifiers */ +public class ModifierPriorityCommand { + private static final Component SUCCESS = new TranslatableComponent("command.tconstruct.modifier_priority"); + + /** + * Registers this sub command with the root command + * @param subCommand Command builder + */ + public static void register(LiteralArgumentBuilder subCommand) { + subCommand.requires(sender -> sender.hasPermission(MantleCommand.PERMISSION_EDIT_SPAWN)) + // no argument: list all priorities + .executes(context -> run(context, false)) + // argument: list only priorities of modifiers using that hook + .then(Commands.argument("modifier_hook", ModifierHookArgument.modifierHook()) + .executes(context -> run(context, true))); + } + + /** + * Runs the command + * @param context Command context + * @param filtered If true, filter the modifiers based on the hook argument + * @return Number of modifiers that are in the output table + */ + private static int run(CommandContext context, boolean filtered) { + // common table properties + TablePrinter table = new TablePrinter<>(); + table.header("ID", m -> m.getId().toString()); + table.header("Priority", m -> Integer.toString(m.getPriority())); + Stream modifiers = ModifierManager.INSTANCE.getAllValues(); + StringBuilder builder = new StringBuilder(); + builder.append("Modifier Priorities"); + + // if filtered, show fewer modifiers and add to the log name + if (filtered) { + ModifierHook filter = ModifierHookArgument.getModifier(context, "modifier_hook"); + modifiers = modifiers.filter(m -> m.getHooks().hasHook(filter)); + builder.append(" for ").append(filter.getName()); + } else { + // if not filtered, include a row listing all used hooks + table.header("Hooks", m -> m.getHooks().getAllModules().keySet().stream().map(ModifierHook::getName).sorted().map(ResourceLocation::toString).collect(Collectors.joining(", "))); + } + builder.append(":").append(System.lineSeparator()); + List list = modifiers.sorted(Comparator.comparingInt(Modifier::getPriority).reversed().thenComparing(Modifier::getId)).toList(); + table.add(list); + + table.build(builder); + TConstruct.LOG.info(builder.toString()); + context.getSource().sendSuccess(SUCCESS, true); + return list.size(); + } +} diff --git a/src/main/resources/assets/tconstruct/lang/en_us.json b/src/main/resources/assets/tconstruct/lang/en_us.json index 0d46243a3c9..ad309464f83 100644 --- a/src/main/resources/assets/tconstruct/lang/en_us.json +++ b/src/main/resources/assets/tconstruct/lang/en_us.json @@ -2787,7 +2787,9 @@ "command.tconstruct.stat_type.wrong_type": "Invalid stat type %s, must inherit from %s", "command.tconstruct.material.not_found": "Unknown material %s", "command.tconstruct.modifier.not_found": "Unknown modifier %s", + "command.tconstruct.modifier_hook.not_found": "Unknown modifier hook %s", "command.tconstruct.modifier_usage": "Successfully printed modifier usage to the game log", + "command.tconstruct.modifier_priority": "Successfully printed modifier priorities to the game log", "command.tconstruct.modifiers.success.add.single": "Applied modifier %s to %s's item", "command.tconstruct.modifiers.success.add.multiple": "Applied modifier %s to %s entities",