From 20eb0cbc7bfdb8ebb0bfd0a32ce08fe175ade440 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Wed, 18 Dec 2024 12:18:12 +0000 Subject: [PATCH] fix: rework some commands to not use QuestObjectArgument Just use a simple string argument type for quest object IDs QuestObjectArgument depends on the server quest file being loaded, which breaks using ftbquests commands in MC functions; these are parsed much sooner than the quest file can be loaded. https://github.com/FTBTeam/FTB-Mods-Issues/issues/1422 --- .../ftbquests/command/FTBQuestsCommands.java | 145 +++++++++--------- .../command/QuestObjectArgument.java | 27 ++-- 2 files changed, 85 insertions(+), 87 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/command/FTBQuestsCommands.java b/common/src/main/java/dev/ftb/mods/ftbquests/command/FTBQuestsCommands.java index 73afbd44..f3228702 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/command/FTBQuestsCommands.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/command/FTBQuestsCommands.java @@ -5,6 +5,7 @@ import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import dev.architectury.registry.registries.RegistrarManager; import dev.ftb.mods.ftblibrary.config.Tristate; @@ -41,12 +42,15 @@ import java.util.*; -/** - * @author LatvianModder - */ public class FTBQuestsCommands { - - private static final SimpleCommandExceptionType NO_INVENTORY = new SimpleCommandExceptionType(Component.translatable("commands.ftbquests.command.error.no_inventory")); + public static final SimpleCommandExceptionType NO_FILE = new SimpleCommandExceptionType( + Component.translatable("commands.ftbquests.command.error.no_file")); + public static final DynamicCommandExceptionType NO_OBJECT = new DynamicCommandExceptionType( + (object) -> Component.translatable("commands.ftbquests.command.error.no_object", object)); + public static final DynamicCommandExceptionType INVALID_ID = new DynamicCommandExceptionType( + (id) -> Component.translatable("commands.ftbquests.command.error.invalid_id", id)); + private static final SimpleCommandExceptionType NO_INVENTORY = new SimpleCommandExceptionType( + Component.translatable("commands.ftbquests.command.error.no_inventory")); public static void register(CommandDispatcher dispatcher) { //noinspection ConstantValue @@ -78,20 +82,18 @@ public static void register(CommandDispatcher dispatcher) { .requires(FTBQuestsCommands::hasEditorPermission) .then(Commands.argument("players", EntityArgument.players()) .then(Commands.literal("reset") - .then(Commands.argument("quest_object", QuestObjectArgument.questObject()) + .then(Commands.argument("quest_object", StringArgumentType.string()) .executes(ctx -> { Collection players = EntityArgument.getPlayers(ctx, "players"); - QuestObjectBase questObject = ctx.getArgument("quest_object", QuestObjectBase.class); - return changeProgress(ctx.getSource(), players, true, questObject); + return changeProgress(ctx.getSource(), players, true, StringArgumentType.getString(ctx, "quest_object")); }) ) ) .then(Commands.literal("complete") - .then(Commands.argument("quest_object", QuestObjectArgument.questObject()) + .then(Commands.argument("quest_object", StringArgumentType.string()) .executes(ctx -> { Collection players = EntityArgument.getPlayers(ctx, "players"); - QuestObjectBase questObject = ctx.getArgument("quest_object", QuestObjectBase.class); - return changeProgress(ctx.getSource(), players, false, questObject); + return changeProgress(ctx.getSource(), players, false, StringArgumentType.getString(ctx, "quest_object")); }) ) ) @@ -99,22 +101,14 @@ public static void register(CommandDispatcher dispatcher) { ) .then(Commands.literal("export_reward_table_to_chest") .requires(FTBQuestsCommands::hasEditorPermission) - .then(Commands.argument("reward_table", QuestObjectArgument.questObject()) - .executes(ctx -> { - QuestObjectBase table = ctx.getArgument("reward_table", QuestObjectBase.class); - if (!(table instanceof RewardTable)) { - throw QuestObjectArgument.NO_OBJECT.create(table.getCodeString()); - } - return exportRewards(ctx.getSource(), (RewardTable) table, null); - }) + .then(Commands.argument("reward_table", StringArgumentType.string()) + .executes(ctx -> + exportRewards(ctx.getSource(), StringArgumentType.getString(ctx, "reward_table"), null) + ) .then(Commands.argument("pos", BlockPosArgument.blockPos()) .executes(ctx -> { - QuestObjectBase table = ctx.getArgument("reward_table", QuestObjectBase.class); BlockPos pos = BlockPosArgument.getSpawnablePos(ctx, "pos"); - if (!(table instanceof RewardTable)) { - throw QuestObjectArgument.NO_OBJECT.create(table.getCodeString()); - } - return exportRewards(ctx.getSource(), (RewardTable) table, pos); + return exportRewards(ctx.getSource(), StringArgumentType.getString(ctx, "reward_table"), pos); }) ) ) @@ -154,8 +148,8 @@ public static void register(CommandDispatcher dispatcher) { ) .then(Commands.literal("open_book") .executes(c -> openQuest(c.getSource().getPlayerOrException(), null)) - .then(Commands.argument("quest_object", QuestObjectArgument.questObject(qob -> qob instanceof QuestObject)) - .executes(c -> openQuest(c.getSource().getPlayerOrException(), c.getArgument("quest_object", QuestObjectBase.class)))) + .then(Commands.argument("quest_object", StringArgumentType.string()) + .executes(c -> openQuest(c.getSource().getPlayerOrException(), StringArgumentType.getString(c, "quest_object")))) ) .then(Commands.literal("clear_item_display_cache") .requires(FTBQuestsCommands::hasEditorPermission) @@ -170,19 +164,31 @@ private static boolean hasEditorPermission(CommandSourceStack stack) { || stack.isPlayer() && PermissionsHelper.hasEditorPermission(stack.getPlayer(), false); } - private static int openQuest(ServerPlayer player, QuestObjectBase qob) { - if (qob == null) { - // just open the book to wherever it last was - new OpenQuestBookMessage(0L).sendTo(player); - } else if (qob instanceof QuestObject quest) { - if (canSeeQuestObject(player, quest)) { - new OpenQuestBookMessage(quest.id).sendTo(player); + private static QuestObjectBase getQuestObjectForString(String idStr) throws CommandSyntaxException { + ServerQuestFile file = ServerQuestFile.INSTANCE; + if (file == null) { + throw NO_FILE.create(); + } + + if (idStr.startsWith("#")) { + String val = idStr.substring(1); + for (QuestObjectBase qob : file.getAllObjects()) { + if (qob.hasTag(val)) { + return qob; + } + } + throw NO_OBJECT.create(idStr); + } else { + long id = QuestObjectBase.parseHexId(idStr).orElseThrow(() -> INVALID_ID.create(idStr)); + QuestObjectBase qob = file.getBase(id); + if (qob == null) { + throw NO_OBJECT.create(idStr); } + return qob; } - return 1; } - private static boolean canSeeQuestObject(ServerPlayer player, QuestObject qo) { + private static boolean playerCanSeeQuestObject(ServerPlayer player, QuestObject qo) { if (qo instanceof Chapter) { return true; } @@ -198,34 +204,47 @@ private static boolean canSeeQuestObject(ServerPlayer player, QuestObject qo) { return quest != null && (data.getCanEdit(player) || !quest.hideDetailsUntilStartable() || data.canStartTasks(quest)); } - private static int exportRewards(CommandSourceStack source, RewardTable table, BlockPos pos) throws CommandSyntaxException { + private static int openQuest(ServerPlayer player, String qobId) throws CommandSyntaxException { + if (qobId == null) { + new OpenQuestBookMessage(0L).sendTo(player); + return Command.SINGLE_SUCCESS; + } else { + if (getQuestObjectForString(qobId) instanceof Quest quest && playerCanSeeQuestObject(player, quest)) { + new OpenQuestBookMessage(quest.id).sendTo(player); + return Command.SINGLE_SUCCESS; + } + } + return 0; + } + + private static int exportRewards(CommandSourceStack source, String idStr, @Nullable BlockPos pos) throws CommandSyntaxException { ServerPlayer player = source.getPlayerOrException(); ServerLevel level = source.getLevel(); - if (pos == null) { - pos = BlockPos.containing(player.pick(10, 1F, false).getLocation()); + if (!(getQuestObjectForString(idStr) instanceof RewardTable table)) { + throw NO_OBJECT.create(idStr); } - BlockEntity be = level.getBlockEntity(pos); - if (!(be instanceof BaseContainerBlockEntity container)) { + pos = Objects.requireNonNullElse(pos, BlockPos.containing(player.pick(10, 1F, false).getLocation())); + if (!(level.getBlockEntity(pos) instanceof BaseContainerBlockEntity container)) { throw NO_INVENTORY.create(); } container.clearContent(); - int s = 0; + int slot = 0; for (WeightedReward wr : table.getWeightedRewards()) { - if (s >= container.getContainerSize()) { + if (slot >= container.getContainerSize()) { source.sendFailure(Component.translatable("commands.ftbquests.command.feedback.table_too_many_items", table.getTitle())); return 0; } else if (wr.getReward() instanceof ItemReward itemReward) { - container.setItem(s++, itemReward.getItem()); + container.setItem(slot++, itemReward.getItem()); } } source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.table_imported", table.getTitle(), table.getWeightedRewards().size()), false); - return 1; + return Command.SINGLE_SUCCESS; } private static int importRewards(CommandSourceStack source, String name, BlockPos pos) throws CommandSyntaxException { @@ -259,7 +278,7 @@ private static int importRewards(CommandSourceStack source, String name, BlockPo source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.table_imported", name, table.getWeightedRewards().size()), false); - return 1; + return Command.SINGLE_SUCCESS; } private static int editingMode(CommandSourceStack source, ServerPlayer player, @Nullable Boolean canEdit) { @@ -270,14 +289,7 @@ private static int editingMode(CommandSourceStack source, ServerPlayer player, @ } data.setCanEdit(player, canEdit); - - if (canEdit) { - source.sendSuccess(() -> Component.translatable("commands.ftbquests.editing_mode.enabled", player.getDisplayName()), true); - } else { - source.sendSuccess(() -> Component.translatable("commands.ftbquests.editing_mode.disabled", player.getDisplayName()), true); - } - - return 1; + return Command.SINGLE_SUCCESS; } private static int locked(CommandSourceStack source, ServerPlayer player, @Nullable Boolean locked) { @@ -289,16 +301,11 @@ private static int locked(CommandSourceStack source, ServerPlayer player, @Nulla data.setLocked(locked); - if (locked) { - source.sendSuccess(() -> Component.translatable("commands.ftbquests.locked.enabled", player.getDisplayName()), true); - } else { - source.sendSuccess(() -> Component.translatable("commands.ftbquests.locked.disabled", player.getDisplayName()), true); - } - - return 1; + return Command.SINGLE_SUCCESS; } - private static int changeProgress(CommandSourceStack source, Collection players, boolean reset, QuestObjectBase questObject) { + private static int changeProgress(CommandSourceStack source, Collection players, boolean reset, String idStr) throws CommandSyntaxException { + QuestObjectBase questObject = getQuestObjectForString(idStr); for (ServerPlayer player : players) { ProgressChange progressChange = new ProgressChange(ServerQuestFile.INSTANCE, questObject, player.getUUID()).setReset(reset); questObject.forceProgress(ServerQuestFile.INSTANCE.getOrCreateTeamData(player), progressChange); @@ -313,7 +320,7 @@ private static int deleteEmptyRewardTables(CommandSourceStack source) { source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.delete_empty_reward_tables.text", removed), false); - return 1; + return Command.SINGLE_SUCCESS; } private static int generateAllItemChapter(CommandSourceStack source) { @@ -337,7 +344,7 @@ private static int generateAllItemChapter(CommandSourceStack source) { .filter(stack -> !stack.isEmpty() && RegistrarManager.getId(stack.getItem(), Registries.ITEM) != null) .sorted(Comparator.comparing(a -> RegistrarManager.getId(a.getItem(), Registries.ITEM))) .toList(); - FTBQuests.LOGGER.info("Found " + allItems.size() + " items in total, chapter ID: " + chapter); + FTBQuests.LOGGER.info("Found {} items in total, chapter ID: {}", allItems.size(), chapter); if (list.isEmpty()) { return 0; @@ -380,7 +387,7 @@ private static int generateAllItemChapter(CommandSourceStack source) { ServerQuestFile.INSTANCE.markDirty(); ServerQuestFile.INSTANCE.saveNow(); source.sendSuccess(() -> Component.literal("Done!"), false); - return 1; + return Command.SINGLE_SUCCESS; } private static final Set warnedPlayers = new HashSet<>(); @@ -391,7 +398,7 @@ private static int doReload(CommandSourceStack source) { if (sender != null && !instance.getOrCreateTeamData(sender).getCanEdit(sender)) { source.sendFailure(Component.translatable("commands.ftbquests.command.error.not_editing")); - return 1; + return 0; } instance.load(); @@ -406,7 +413,7 @@ private static int doReload(CommandSourceStack source) { warnedPlayers.add(id); } - return 1; + return Command.SINGLE_SUCCESS; } private static int toggleRewardBlocking(CommandSourceStack source, ServerPlayer player, Boolean doBlocking) { @@ -415,17 +422,15 @@ private static int toggleRewardBlocking(CommandSourceStack source, ServerPlayer if (doBlocking == null) { doBlocking = !data.areRewardsBlocked(); } - data.setRewardsBlocked(doBlocking); source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.rewards_blocked", data, data.areRewardsBlocked()), false); - - return 1; + return Command.SINGLE_SUCCESS; } private static int clearDisplayCache(CommandSourceStack source) { ClearDisplayCacheMessage.clearForAll(source.getServer()); source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.clear_display_cache"), false); - return 1; + return Command.SINGLE_SUCCESS; } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/command/QuestObjectArgument.java b/common/src/main/java/dev/ftb/mods/ftbquests/command/QuestObjectArgument.java index 160712d4..b292f256 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/command/QuestObjectArgument.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/command/QuestObjectArgument.java @@ -5,8 +5,6 @@ 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.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import dev.ftb.mods.ftbquests.client.ClientQuestFile; @@ -14,7 +12,6 @@ import dev.ftb.mods.ftbquests.quest.QuestObjectBase; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.network.chat.Component; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -22,22 +19,18 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; +/** + * Avoid using this in commands, since it breaks MC functions; functions are parsed by the server very early on, + * long before the server quest file can possibly be loaded. + * Will be removed sometime after 1.21.1 + */ +@Deprecated(forRemoval = true) public class QuestObjectArgument implements ArgumentType { - private static final List examples = ImmutableList.of( "1CF239D256879E6F", "#importantquests" ); - public static final SimpleCommandExceptionType NO_FILE = new SimpleCommandExceptionType( - Component.translatable("commands.ftbquests.command.error.no_file")); - - public static final DynamicCommandExceptionType NO_OBJECT = new DynamicCommandExceptionType( - (object) -> Component.translatable("commands.ftbquests.command.error.no_object", object)); - - public static final DynamicCommandExceptionType INVALID_ID = new DynamicCommandExceptionType( - (id) -> Component.translatable("commands.ftbquests.command.error.invalid_id", id)); - private final Predicate filter; public QuestObjectArgument() { @@ -60,21 +53,21 @@ public QuestObjectBase parse(StringReader reader) throws CommandSyntaxException return object; } } - throw NO_OBJECT.createWithContext(reader, id); + throw FTBQuestsCommands.NO_OBJECT.createWithContext(reader, id); } else { try { long num = file.getID(id); QuestObjectBase object = file.getBase(num); if (object == null || !filter.test(object)) { - throw NO_OBJECT.createWithContext(reader, id); + throw FTBQuestsCommands.NO_OBJECT.createWithContext(reader, id); } return object; } catch (NumberFormatException e) { - throw INVALID_ID.createWithContext(reader, id); + throw FTBQuestsCommands.INVALID_ID.createWithContext(reader, id); } } } - throw NO_FILE.create(); + throw FTBQuestsCommands.NO_FILE.create(); } @Override