From 4e3e7153bb1ee7af80c0aae0a1793cf07681c390 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 7 Nov 2024 12:27:46 +0000 Subject: [PATCH 01/24] fix: changelog versions should be level 2 headings! [ciskip] --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 099b43c2..8300b625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [2101.1.1] +## [2101.1.1] ### Added * The pinned quests panel now has a "Pinned Quests" title for clarity @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed context menu tooltips sometimes appearing behind the context menu * Fixed some issues with the reward table editor GUI (changes not getting correctly sync'd to server in some cases) -# [2101.1.0] +## [2101.1.0] ### Changed * Minecraft 1.21.1 is now required; this no longer supports Minecraft 1.21 @@ -36,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added new "Hide Quests until Dependencies Complete" setting * So there are now two independent setting for hiding quests based on dependency visibility and/or completion -# [2100.1.5] +## [2100.1.5] ### Changed * FTB Quests items are now registered to the `FTB Suite` creative tab instead of FTB Quests own tab @@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * In addition, rotated images with a non-default aspect ratio now preview correctly during rotation * Fixed copy/pasting images -# [2100.1.4] +## [2100.1.4] ### Fixed * Fixed coloured text in quest titles & subtitles not showing in the quest view panel @@ -57,21 +57,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Pre-existing chapter files may be named after the hex chapter id; they will still work fine, but you can rename them if you wish * If you choose to rename them, also update the `filename` field in the file correspondingly -# [2100.1.3] +## [2100.1.3] ### Fixed * Fixed adding tasks to existing quests sometimes losing the task type (leading to a '?' button appearing) * Fixed images in the quest book not sync'ing to the client * Fixed issue where using FTB Filter System filters would sometimes fail to find matching items for GUI display -# [2100.1.2] +## [2100.1.2] ### Fixed * Fixed raw json text in quest descriptions not always being recognised * Fixed a packet sync error related to translation system when on dedicated server * Chapter filenames are now again named after the chapter title (at the time of creation), as they used to be in 1.20 and earlier -# [2100.1.1] +## [2100.1.1] ### Fixed * Fixed chapter and chapter group creation popups moving in and out with the chapter panel when it's not pinned. @@ -82,7 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Removed a misleading "Click to Submit" tooltip from fluid tasks in the quest view panel * Fluid tasks can only be submitted via a Task Screen -# [2100.1.0] +## [2100.1.0] ### Changed * Ported to Minecraft 1.21. Support for Fabric and NeoForge. @@ -94,19 +94,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Text which doesn't have a translation in the current locale (but does in the `en_us` locale) is highlighted when in edit mode. * Changes do not affect the player experience -# [2004.2.1] +## [2004.2.1] ### Added * The pinned quests panel positioned can now be adjusted in client config (use "Player Preferences" button in lower right of screen) * A couple of other minor GUI fixes and improvements (mainly via FTB Library) -# [2004.2.0] +## [2004.2.0] ### Changed * Ported to Minecraft 1.20.4. Supported on Forge, NeoForge and Fabric. * Some GUI enhancements in a few places. -# [2001.3.4] +## [2001.3.4] ### Added * The color of dependency lines for uncompleted quests is now themable From 0f9f4b8e37072b1e6f00520b0855465cbe081728 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 8 Nov 2024 12:37:26 +0000 Subject: [PATCH 02/24] fix: QuestFile#getOrCreateTeamData(Player) safety Deprecated QuestFile#getOrCreateTeamData(Player) in favour of getTeamData(Player player), which returns an Optional. It's possible for this to empty if FTB Teams hasn't init'd the player data yet, which can happen when new players join the server. https://github.com/FTBTeam/FTB-Mods-Issues/issues/1383 --- .../mods/ftbquests/FTBQuestsEventHandler.java | 64 ++++++-------- .../dev/ftb/mods/ftbquests/api/QuestFile.java | 39 +++++++++ .../mods/ftbquests/block/TaskScreenBlock.java | 2 +- .../block/entity/DetectorBlockEntity.java | 5 +- .../block/entity/QuestBarrierBlockEntity.java | 4 +- .../ftbquests/command/FTBQuestsCommands.java | 86 +++++++++---------- .../ftbquests/net/ClaimAllRewardsMessage.java | 17 ++-- .../net/ClaimChoiceRewardMessage.java | 16 ++-- .../ftbquests/net/ClaimRewardMessage.java | 14 ++- .../ftbquests/net/RequestTeamDataMessage.java | 7 +- .../mods/ftbquests/net/SubmitTaskMessage.java | 14 +-- .../net/ToggleChapterPinnedMessage.java | 10 +-- .../net/ToggleEditingModeMessage.java | 6 +- .../ftbquests/net/TogglePinnedMessage.java | 10 +-- .../mods/ftbquests/quest/BaseQuestFile.java | 6 ++ .../dev/ftb/mods/ftbquests/quest/Quest.java | 5 ++ .../ftb/mods/ftbquests/quest/QuestLink.java | 5 ++ .../ftb/mods/ftbquests/quest/QuestObject.java | 4 + .../ftb/mods/ftbquests/quest/TeamData.java | 10 +-- .../mods/ftbquests/quest/task/StageTask.java | 19 ++-- .../ftb/mods/ftbquests/quest/task/Task.java | 5 ++ .../dev/ftb/mods/ftbquests/util/NetUtils.java | 4 +- 22 files changed, 201 insertions(+), 151 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/FTBQuestsEventHandler.java b/common/src/main/java/dev/ftb/mods/ftbquests/FTBQuestsEventHandler.java index 4dba754f..c707f177 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/FTBQuestsEventHandler.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/FTBQuestsEventHandler.java @@ -8,7 +8,6 @@ import dev.ftb.mods.ftbquests.events.ClearFileCacheEvent; import dev.ftb.mods.ftbquests.quest.BaseQuestFile; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbquests.quest.task.DimensionTask; import dev.ftb.mods.ftbquests.quest.task.KillTask; import dev.ftb.mods.ftbquests.quest.task.Task; @@ -124,13 +123,13 @@ private EventResult playerKill(LivingEntity entity, DamageSource source) { return EventResult.pass(); } - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - - for (KillTask task : killTasks) { - if (data.getProgress(task) < task.getMaxProgress() && data.canStartTasks(task.getQuest())) { - task.kill(data, entity); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + for (KillTask task : killTasks) { + if (data.getProgress(task) < task.getMaxProgress() && data.canStartTasks(task.getQuest())) { + task.kill(data, entity); + } } - } + }); } return EventResult.pass(); @@ -139,34 +138,29 @@ private EventResult playerKill(LivingEntity entity, DamageSource source) { private void playerTick(Player player) { ServerQuestFile file = ServerQuestFile.INSTANCE; - if (player instanceof ServerPlayer && file != null && !PlayerHooks.isFake(player)) { + if (player instanceof ServerPlayer serverPlayer && file != null && !PlayerHooks.isFake(player)) { if (autoSubmitTasks == null) { - autoSubmitTasks = file.collect(o -> o instanceof Task && ((Task) o).autoSubmitOnPlayerTick() > 0); + autoSubmitTasks = file.collect(o -> o instanceof Task t && t.autoSubmitOnPlayerTick() > 0); } - // Don't be deceived, its somehow possible to be null here + // Don't be deceived, it's somehow possible to be null here if (autoSubmitTasks == null || autoSubmitTasks.isEmpty()) { return; } - TeamData data = file.getOrCreateTeamData(player); - - if (data.isLocked()) { - return; - } - - long t = player.level().getGameTime(); + file.getTeamData(player).ifPresent(data -> { + long now = player.level().getGameTime(); - file.withPlayerContext((ServerPlayer) player, () -> { - for (Task task : autoSubmitTasks) { - long d = task.autoSubmitOnPlayerTick(); - - if (d > 0L && t % d == 0L) { - if (!data.isCompleted(task) && data.canStartTasks(task.getQuest())) { - task.submitTask(data, (ServerPlayer) player); + file.withPlayerContext(serverPlayer, () -> { + for (Task task : autoSubmitTasks) { + long interval = task.autoSubmitOnPlayerTick(); + if (interval > 0L && now % interval == 0L) { + if (!data.isCompleted(task) && data.canStartTasks(task.getQuest())) { + task.submitTask(data, serverPlayer); + } } } - } + }); }); } } @@ -206,17 +200,15 @@ private void cloned(ServerPlayer oldPlayer, ServerPlayer newPlayer, boolean wonG private void changedDimension(ServerPlayer player, ResourceKey oldLevel, ResourceKey newLevel) { if (!PlayerHooks.isFake(player)) { ServerQuestFile file = ServerQuestFile.INSTANCE; - TeamData data = file.getOrCreateTeamData(player); - - if (data.isLocked()) { - return; - } - - file.withPlayerContext(player, () -> { - for (DimensionTask task : file.collect(DimensionTask.class)) { - if (data.canStartTasks(task.getQuest())) { - task.submitTask(data, player); - } + file.getTeamData(player).ifPresent(data -> { + if (!data.isLocked()) { + file.withPlayerContext(player, () -> { + for (DimensionTask task : file.collect(DimensionTask.class)) { + if (data.canStartTasks(task.getQuest())) { + task.submitTask(data, player); + } + } + }); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/api/QuestFile.java b/common/src/main/java/dev/ftb/mods/ftbquests/api/QuestFile.java index 094eb110..c57eeb2f 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/api/QuestFile.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/api/QuestFile.java @@ -6,25 +6,64 @@ import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbteams.api.Team; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; public interface QuestFile { + /** + * {@return true if this quest file is on the server, false if on the client} + */ boolean isServerSide(); + /** + * Check if this quest file is editable. This will always return false on the server; + * use {@link TeamData#getCanEdit(Player)} there. + * @return true if the file can be edited (clientside), false otherwise + */ boolean canEdit(); + /** + * {@return FTB Quests progress data for the given FTB Teams team UUID, or null if there is no data for this team ID} + * @param id the team ID to check + */ @Nullable TeamData getNullableTeamData(UUID id); + /** + * {@return FTB Quests progress data for the given FTB Teams team UUID, creating new progress data if necessary} + * @param teamId the team ID to check + */ TeamData getOrCreateTeamData(UUID teamId); + /** + * {@return FTB Quests progress data for the given FTB Teams team, creating new progress data if necessary} + * @param team the team to check + */ TeamData getOrCreateTeamData(Team team); + /** + * {@return quest team data for the player, creating new progress data if necessary} + * @param player the player + * @deprecated not recommended since it can return null if player's team data isn't ready. Use {@link #getTeamData(Player)}. + */ + @Nullable + @Deprecated(forRemoval = true) TeamData getOrCreateTeamData(Entity player); + /** + * Get the FTB Quests team progress data for the given player, creating new progress data if necessary. + * @param player the player to check + * @return the team data, or {@code Optional.empty()} if the player isn't yet known by FTB Teams (rare, but possible, particularly for players joining the server for the first time) + */ + Optional getTeamData(Player player); + + /** + * {@return collection of progress data for all known teams} + */ Collection getAllTeamData(); void forAllChapters(Consumer consumer); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/block/TaskScreenBlock.java b/common/src/main/java/dev/ftb/mods/ftbquests/block/TaskScreenBlock.java index 87feff01..74b363e4 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/block/TaskScreenBlock.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/block/TaskScreenBlock.java @@ -130,7 +130,7 @@ public void setPlacedBy(Level level, BlockPos blockPos, BlockState blockState, @ if (level.getBlockEntity(blockPos) instanceof TaskScreenBlockEntity coreScreen) { if (livingEntity instanceof ServerPlayer sp) { - coreScreen.setTeamId(ServerQuestFile.INSTANCE.getOrCreateTeamData(sp).getTeamId()); + ServerQuestFile.INSTANCE.getTeamData(sp).ifPresent(d -> coreScreen.setTeamId(d.getTeamId())); } Direction facing = blockState.getValue(FACING); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/DetectorBlockEntity.java b/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/DetectorBlockEntity.java index 65770abf..b041e217 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/DetectorBlockEntity.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/DetectorBlockEntity.java @@ -3,7 +3,6 @@ import dev.architectury.hooks.level.entity.PlayerHooks; import dev.ftb.mods.ftbquests.quest.QuestObjectBase; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbquests.registry.ModBlockEntityTypes; import dev.ftb.mods.ftbquests.util.ProgressChange; import net.minecraft.core.BlockPos; @@ -50,8 +49,8 @@ public void onPowered(Level level, BlockPos pos) { if (qo != null) { AABB box = new AABB(pos).inflate(radius); for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, box, DetectorBlockEntity::isRealPlayer)) { - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - qo.forceProgressRaw(data, new ProgressChange(qo, player.getUUID()).setReset(false).withNotifications()); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> + qo.forceProgressRaw(data, new ProgressChange(qo, player.getUUID()).setReset(false).withNotifications())); } } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/QuestBarrierBlockEntity.java b/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/QuestBarrierBlockEntity.java index 05c280b9..9d1a9988 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/QuestBarrierBlockEntity.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/block/entity/QuestBarrierBlockEntity.java @@ -66,6 +66,8 @@ public void update(String s) { public boolean isOpen(Player player) { BaseQuestFile file = FTBQuestsAPI.api().getQuestFile(player.level().isClientSide()); QuestObject qo = file.get(objId); - return qo != null && file.getOrCreateTeamData(player).isCompleted(qo); + return qo != null && file.getTeamData(player) + .map(d -> d.isCompleted(qo)) + .orElse(false); } } 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 a9edfdcd..d22d987b 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 @@ -18,7 +18,6 @@ import dev.ftb.mods.ftbquests.quest.loot.WeightedReward; import dev.ftb.mods.ftbquests.quest.reward.ItemReward; import dev.ftb.mods.ftbquests.quest.task.ItemTask; -import dev.ftb.mods.ftbquests.quest.task.Task; import dev.ftb.mods.ftbquests.util.ProgressChange; import net.minecraft.ChatFormatting; import net.minecraft.Util; @@ -187,16 +186,10 @@ private static boolean canSeeQuestObject(ServerPlayer player, QuestObject qo) { if (qo instanceof Chapter) { return true; } - TeamData data = TeamData.get(player); - Quest quest = null; - if (qo instanceof QuestLink link) { - quest = link.getQuest().orElse(null); - } else if (qo instanceof Task task) { - quest = task.getQuest(); - } else if (qo instanceof Quest q) { - quest = q; - } - return quest != null && (data.getCanEdit(player) || !quest.hideDetailsUntilStartable() || data.canStartTasks(quest)); + return ServerQuestFile.INSTANCE.getTeamData(player).map(data -> { + Quest quest = qo.getRelatedQuest(); + return quest != null && (data.getCanEdit(player) || !quest.hideDetailsUntilStartable() || data.canStartTasks(quest)); + }).orElse(false); } private static int exportRewards(CommandSourceStack source, RewardTable table, BlockPos pos) throws CommandSyntaxException { @@ -264,45 +257,43 @@ private static int importRewards(CommandSourceStack source, String name, BlockPo } private static int editingMode(CommandSourceStack source, ServerPlayer player, @Nullable Boolean canEdit) { - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - - if (canEdit == null) { - canEdit = !data.getCanEdit(player); - } + return ServerQuestFile.INSTANCE.getTeamData(player).map(data -> { + boolean newCanEdit = Objects.requireNonNullElse(canEdit, !data.getCanEdit(player)); - data.setCanEdit(player, canEdit); + data.setCanEdit(player, newCanEdit); - 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); - } + if (newCanEdit) { + 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 1; + }).orElse(0); } private static int locked(CommandSourceStack source, ServerPlayer player, @Nullable Boolean locked) { - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); + return ServerQuestFile.INSTANCE.getTeamData(player).map(data -> { + boolean newLocked = Objects.requireNonNullElse(locked, !data.isLocked()); - if (locked == null) { - locked = !data.isLocked(); - } + data.setLocked(newLocked); - 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); - } + if (newLocked) { + 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 1; + }).orElse(0); } private static int changeProgress(CommandSourceStack source, Collection players, boolean reset, QuestObjectBase questObject) { for (ServerPlayer player : players) { - ProgressChange progressChange = new ProgressChange(questObject, player.getUUID()).setReset(reset); - questObject.forceProgress(ServerQuestFile.INSTANCE.getOrCreateTeamData(player), progressChange); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + ProgressChange progressChange = new ProgressChange(questObject, player.getUUID()).setReset(reset); + questObject.forceProgress(data, progressChange); + }); } source.sendSuccess(() -> Component.translatable("commands.ftbquests.change_progress.text"), false); @@ -388,7 +379,12 @@ private static int doReload(CommandSourceStack source) { ServerQuestFile instance = ServerQuestFile.INSTANCE; ServerPlayer sender = source.getPlayer(); - if (sender != null && !instance.getOrCreateTeamData(sender).getCanEdit(sender)) { + Optional playerData = instance.getTeamData(sender); + if (playerData.isEmpty()) { + return 0; + } + + if (sender != null && !playerData.get().getCanEdit(sender)) { source.sendFailure(Component.translatable("commands.ftbquests.command.error.not_editing")); return 1; } @@ -411,17 +407,15 @@ private static int doReload(CommandSourceStack source) { } private static int toggleRewardBlocking(CommandSourceStack source, ServerPlayer player, Boolean doBlocking) { - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - - if (doBlocking == null) { - doBlocking = !data.areRewardsBlocked(); - } + return ServerQuestFile.INSTANCE.getTeamData(player).map(data -> { + boolean shouldBlock = Objects.requireNonNullElse(doBlocking, !data.areRewardsBlocked()); + data.setRewardsBlocked(shouldBlock); - data.setRewardsBlocked(doBlocking); + source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.rewards_blocked", data, data.areRewardsBlocked()), false); - source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.rewards_blocked", data, data.areRewardsBlocked()), false); + return 1; + }).orElse(0); - return 1; } private static int clearDisplayCache(CommandSourceStack source) { diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimAllRewardsMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimAllRewardsMessage.java index 10fe39c8..ee21da08 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimAllRewardsMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimAllRewardsMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -24,14 +23,14 @@ public Type type() { public static void handle(ClaimAllRewardsMessage message, NetworkManager.PacketContext context) { context.queue(() -> { ServerPlayer player = (ServerPlayer) context.getPlayer(); - TeamData data = TeamData.get(player); - - ServerQuestFile.INSTANCE.forAllQuests(quest -> { - if (data.isCompleted(quest)) { - quest.getRewards().stream() - .filter(reward -> !reward.getExcludeFromClaimAll()) - .forEach(reward -> data.claimReward(player, reward, true)); - } + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + data.getFile().forAllQuests(quest -> { + if (data.isCompleted(quest)) { + quest.getRewards().stream() + .filter(reward -> !reward.getExcludeFromClaimAll()) + .forEach(reward -> data.claimReward(player, reward, true)); + } + }); }); }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimChoiceRewardMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimChoiceRewardMessage.java index fb70ae8a..f617f21e 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimChoiceRewardMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimChoiceRewardMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbquests.quest.loot.RewardTable; import dev.ftb.mods.ftbquests.quest.reward.ChoiceReward; import dev.ftb.mods.ftbquests.quest.reward.Reward; @@ -32,15 +31,16 @@ public static void handle(ClaimChoiceRewardMessage message, NetworkManager.Packe Reward reward = ServerQuestFile.INSTANCE.getReward(message.id); if (reward instanceof ChoiceReward choiceReward && context.getPlayer() instanceof ServerPlayer serverPlayer) { - TeamData data = TeamData.get(serverPlayer); - RewardTable table = choiceReward.getTable(); + ServerQuestFile.INSTANCE.getTeamData(serverPlayer).ifPresent(data -> { + RewardTable table = choiceReward.getTable(); - if (table != null && data.isCompleted(reward.getQuest())) { - if (message.index >= 0 && message.index < table.getWeightedRewards().size()) { - table.getWeightedRewards().get(message.index).getReward().claim(serverPlayer, true); - data.claimReward(serverPlayer, reward, true); + if (table != null && data.isCompleted(reward.getQuest())) { + if (message.index >= 0 && message.index < table.getWeightedRewards().size()) { + table.getWeightedRewards().get(message.index).getReward().claim(serverPlayer, true); + data.claimReward(serverPlayer, reward, true); + } } - } + }); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimRewardMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimRewardMessage.java index 40b2ea5c..cc23ab4a 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimRewardMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/ClaimRewardMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbquests.quest.reward.Reward; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; @@ -29,13 +28,12 @@ public static void handle(ClaimRewardMessage message, NetworkManager.PacketConte context.queue(() -> { Reward reward = ServerQuestFile.INSTANCE.getReward(message.id); - if (reward != null) { - ServerPlayer player = (ServerPlayer) context.getPlayer(); - TeamData teamData = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - - if (teamData.isCompleted(reward.getQuest())) { - teamData.claimReward(player, reward, message.shouldNotify); - } + if (reward != null && context.getPlayer() instanceof ServerPlayer player) { + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(teamData -> { + if (teamData.isCompleted(reward.getQuest())) { + teamData.claimReward(player, reward, message.shouldNotify); + } + }); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/RequestTeamDataMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/RequestTeamDataMessage.java index 6d45f45a..a70f06c8 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/RequestTeamDataMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/RequestTeamDataMessage.java @@ -2,7 +2,7 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; -import dev.ftb.mods.ftbquests.quest.TeamData; +import dev.ftb.mods.ftbquests.quest.ServerQuestFile; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -20,10 +20,11 @@ public Type type() { return TYPE; } - public static void handle(RequestTeamDataMessage message, NetworkManager.PacketContext context) { + public static void handle(RequestTeamDataMessage ignoredMessage, NetworkManager.PacketContext context) { context.queue(() -> { if (context.getPlayer() instanceof ServerPlayer serverPlayer) { - NetworkManager.sendToPlayer(serverPlayer, new SyncTeamDataMessage(TeamData.get(serverPlayer))); + ServerQuestFile.INSTANCE.getTeamData(serverPlayer) + .ifPresent(data -> NetworkManager.sendToPlayer(serverPlayer, new SyncTeamDataMessage(data))); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/SubmitTaskMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/SubmitTaskMessage.java index b46235b8..5e8922d0 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/SubmitTaskMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/SubmitTaskMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import dev.ftb.mods.ftbquests.quest.task.Task; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; @@ -27,13 +26,14 @@ public Type type() { public static void handle(SubmitTaskMessage message, NetworkManager.PacketContext context) { if (context.getPlayer() instanceof ServerPlayer player) { context.queue(() -> { - TeamData data = TeamData.get(player); - if (!data.isLocked()) { - Task task = data.getFile().getTask(message.taskId); - if (task != null && data.getFile() instanceof ServerQuestFile sqf && data.canStartTasks(task.getQuest())) { - sqf.withPlayerContext(player, () -> task.submitTask(data, player)); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + if (!data.isLocked()) { + Task task = data.getFile().getTask(message.taskId); + if (task != null && data.getFile() instanceof ServerQuestFile sqf && data.canStartTasks(task.getQuest())) { + sqf.withPlayerContext(player, () -> task.submitTask(data, player)); + } } - } + }); }); } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleChapterPinnedMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleChapterPinnedMessage.java index 62d37f59..89a8ad1c 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleChapterPinnedMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleChapterPinnedMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -24,10 +23,11 @@ public Type type() { public static void handle(ToggleChapterPinnedMessage message, NetworkManager.PacketContext context) { context.queue(() -> { ServerPlayer player = (ServerPlayer) context.getPlayer(); - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - boolean newPinned = !data.isChapterPinned(player); - data.setChapterPinned(player, newPinned); - NetworkManager.sendToPlayer(player, new ToggleChapterPinnedResponseMessage(newPinned)); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + boolean newPinned = !data.isChapterPinned(player); + data.setChapterPinned(player, newPinned); + NetworkManager.sendToPlayer(player, new ToggleChapterPinnedResponseMessage(newPinned)); + }); }); } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleEditingModeMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleEditingModeMessage.java index 0d7342fd..89b61b8a 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleEditingModeMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/ToggleEditingModeMessage.java @@ -4,7 +4,6 @@ import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.integration.PermissionsHelper; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -28,8 +27,9 @@ public static void handle(ToggleEditingModeMessage message, NetworkManager.Packe if (PermissionsHelper.hasEditorPermission(player, false) || player.getServer() != null && player.getServer().isSingleplayerOwner(player.getGameProfile())) { - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - data.setCanEdit(player, !data.getCanEdit(player)); // will send a response to the client, causing GUI refresh + // will send a response to the client, causing GUI refresh + ServerQuestFile.INSTANCE.getTeamData(player) + .ifPresent(data -> data.setCanEdit(player, !data.getCanEdit(player))); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/TogglePinnedMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/TogglePinnedMessage.java index ed6cb9ac..a271d2d8 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/TogglePinnedMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/TogglePinnedMessage.java @@ -3,7 +3,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.quest.ServerQuestFile; -import dev.ftb.mods.ftbquests.quest.TeamData; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; @@ -26,10 +25,11 @@ public Type type() { public static void handle(TogglePinnedMessage message, NetworkManager.PacketContext context) { context.queue(() -> { ServerPlayer player = (ServerPlayer) context.getPlayer(); - TeamData data = ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - boolean newPinned = !data.isQuestPinned(player, message.id); - data.setQuestPinned(player, message.id, newPinned); - NetworkManager.sendToPlayer(player, new TogglePinnedResponseMessage(message.id, newPinned)); + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + boolean newPinned = !data.isQuestPinned(player, message.id); + data.setQuestPinned(player, message.id, newPinned); + NetworkManager.sendToPlayer(player, new TogglePinnedResponseMessage(message.id, newPinned)); + }); }); } } \ No newline at end of file diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java index eb1f0479..d06713b0 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java @@ -1087,6 +1087,12 @@ public TeamData getOrCreateTeamData(Entity player) { .orElse(null); } + @Override + public Optional getTeamData(Player player) { + return FTBTeamsAPI.api().getManager().getTeamForPlayerID(player.getUUID()) + .map(this::getOrCreateTeamData); + } + @Override public Collection getAllTeamData() { return Collections.unmodifiableCollection(teamDataMap.values()); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java index 28d37a87..ce975c90 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java @@ -1078,4 +1078,9 @@ public List> buildDescriptionIndex() { return index; } + + @Override + public Quest getRelatedQuest() { + return this; + } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestLink.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestLink.java index 137e0df1..8350c48c 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestLink.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestLink.java @@ -35,6 +35,11 @@ public QuestLink(long id, Chapter chapter, long linkId) { size = 1D; } + @Override + public Quest getRelatedQuest() { + return getQuest().orElse(null); + } + @Override public QuestObjectType getObjectType() { return QuestObjectType.QUEST_LINK; diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestObject.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestObject.java index 76fea128..0e683a25 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestObject.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestObject.java @@ -132,4 +132,8 @@ public boolean isOptionalForProgression() { public boolean hasUnclaimedRewardsRaw(TeamData teamData, UUID player) { return false; } + + public Quest getRelatedQuest() { + return null; + } } \ No newline at end of file diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java index b212590e..fc9b68ec 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java @@ -5,7 +5,6 @@ import dev.ftb.mods.ftblibrary.snbt.SNBT; import dev.ftb.mods.ftblibrary.snbt.SNBTCompoundTag; import dev.ftb.mods.ftbquests.FTBQuests; -import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.client.ClientQuestFile; import dev.ftb.mods.ftbquests.events.QuestProgressEventData; import dev.ftb.mods.ftbquests.net.*; @@ -33,7 +32,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import org.apache.commons.lang3.function.ToBooleanBiFunction; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -115,10 +113,10 @@ public BaseQuestFile getFile() { return file; } - @NotNull - public static TeamData get(Player player) { - return FTBQuestsAPI.api().getQuestFile(player.getCommandSenderWorld().isClientSide()).getOrCreateTeamData(player); - } +// @NotNull +// public static TeamData get(Player player) { +// return FTBQuestsAPI.api().getQuestFile(player.getCommandSenderWorld().isClientSide()).getOrCreateTeamData(player); +// } public void markDirty() { shouldSave = true; diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/StageTask.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/StageTask.java index 8e2c31b9..203a76a2 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/StageTask.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/StageTask.java @@ -78,18 +78,19 @@ public boolean canSubmit(TeamData teamData, ServerPlayer player) { @SuppressWarnings("unused") public static void checkStages(ServerPlayer player) { // hook for FTB XMod Compat to call into - - TeamData data = ServerQuestFile.INSTANCE == null || PlayerHooks.isFake(player) ? null : ServerQuestFile.INSTANCE.getOrCreateTeamData(player); - - if (data == null || data.isLocked()) { + if (ServerQuestFile.INSTANCE == null || PlayerHooks.isFake(player)) { return; } - ServerQuestFile.INSTANCE.withPlayerContext(player, () -> { - for (Task task : ServerQuestFile.INSTANCE.getAllTasks()) { - if (task instanceof StageTask && data.canStartTasks(task.getQuest())) { - task.submitTask(data, player); - } + ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { + if (!data.isLocked()) { + ServerQuestFile.INSTANCE.withPlayerContext(player, () -> { + for (Task task : ServerQuestFile.INSTANCE.getAllTasks()) { + if (task instanceof StageTask && data.canStartTasks(task.getQuest())) { + task.submitTask(data, player); + } + } + }); } }); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/Task.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/Task.java index ec1c919a..3fe164d5 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/Task.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/task/Task.java @@ -53,6 +53,11 @@ public Quest getQuest() { return quest; } + @Override + public Quest getRelatedQuest() { + return quest; + } + @Override public final QuestObjectType getObjectType() { return QuestObjectType.TASK; diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/util/NetUtils.java b/common/src/main/java/dev/ftb/mods/ftbquests/util/NetUtils.java index 87ee2061..36a20abf 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/util/NetUtils.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/util/NetUtils.java @@ -13,7 +13,9 @@ public class NetUtils { public static boolean canEdit(NetworkManager.PacketContext context) { Player player = context.getPlayer(); - return player != null && ServerQuestFile.INSTANCE.getOrCreateTeamData(player).getCanEdit(player); + return player != null + && ServerQuestFile.INSTANCE.getTeamData(player) + .map(d -> d.getCanEdit(player)).orElse(false); } public static void write(FriendlyByteBuf buffer, Collection list, BiConsumer writer) { From 655dbafd2d32edbe8142bca5f39e1401ab73a629 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 8 Nov 2024 12:37:38 +0000 Subject: [PATCH 03/24] build: version -> 2101.1.2, changelog updated --- CHANGELOG.md | 6 ++++++ gradle.properties | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8300b625..ef9a1fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2101.1.2] + +### Fixed +* Fixed a rare server-side crash which can occur when new (as in, never on this server before) players join the server + * Timing issue with FTB Teams initialising their team data for the first time + ## [2101.1.1] ### Added diff --git a/gradle.properties b/gradle.properties index ba678b32..e4b3df63 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ maven_group=dev.ftb.mods mod_author=FTB Team # Build time -mod_version=2101.1.1 +mod_version=2101.1.2 minecraft_version=1.21.1 # Cross env From 507cd8354f1b46192529e78148d23da151bfa616 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Mon, 11 Nov 2024 15:41:00 +0000 Subject: [PATCH 04/24] fix: allow clickthrough for images when left-clicking & no action configured Allows panning while left-clicking over a background image --- .../client/gui/quests/ChapterImageButton.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/ChapterImageButton.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/ChapterImageButton.java index e0b245ee..0edc3a9e 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/ChapterImageButton.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/ChapterImageButton.java @@ -93,6 +93,17 @@ public boolean checkMouseOver(int mouseX, int mouseY) { return super.checkMouseOver(mouseX, mouseY); } + @Override + public boolean mousePressed(MouseButton button) { + if (isMouseOver() && getWidgetType() != WidgetType.DISABLED) { + onClicked(button); + // returning false on left button click allows click-through for panning behaviour + // (also, images with a click action defined should swallow the mouse click) + return !button.isLeft() || !chapterImage.getClick().isEmpty(); + } + return false; + } + @Override public void onClicked(MouseButton button) { if (questScreen.file.canEdit() && button.isRight()) { From 8f408ef4246633da54ac43fd472e6e987deca7a2 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Tue, 19 Nov 2024 12:24:50 +0000 Subject: [PATCH 05/24] fix: don't do Quest#onCompleted work twice Can happen if a quest has multiple optional tasks where completing one task completes the quest. https://github.com/FTBTeam/FTB-Mods-Issues/issues/1390 --- .../dev/ftb/mods/ftbquests/quest/Quest.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java index ce975c90..6cc3a4be 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/Quest.java @@ -510,20 +510,23 @@ public void onStarted(QuestProgressEventData data) { @Override public void onCompleted(QuestProgressEventData data) { - data.setCompleted(id); - ObjectCompletedEvent.QUEST.invoker().act(new ObjectCompletedEvent.QuestEvent(data.withObject(this))); + if (!data.getTeamData().isCompleted(this)) { + // it's possible this quest is already completed, if it has one or more optional tasks + data.setCompleted(id); + ObjectCompletedEvent.QUEST.invoker().act(new ObjectCompletedEvent.QuestEvent(data.withObject(this))); - if (!disableToast) { - data.notifyPlayers(id); - } + if (!disableToast) { + data.notifyPlayers(id); + } - if (chapter.isCompletedRaw(data.getTeamData())) { - chapter.onCompleted(data.withObject(chapter)); - } + if (chapter.isCompletedRaw(data.getTeamData())) { + chapter.onCompleted(data.withObject(chapter)); + } - data.getTeamData().checkAutoCompletion(this); + data.getTeamData().checkAutoCompletion(this); - checkForDependantCompletion(data.getTeamData()); + checkForDependantCompletion(data.getTeamData()); + } } private void checkForDependantCompletion(TeamData data) { From 282aa8d4c7dd6de7513b7eeac7ea78fb338518bf Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Tue, 19 Nov 2024 12:25:51 +0000 Subject: [PATCH 06/24] chore: changelog updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef9a1fbe..8eb98604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Fixed a rare server-side crash which can occur when new (as in, never on this server before) players join the server * Timing issue with FTB Teams initialising their team data for the first time +* Fixed quest completion toasts appearing more than once if a quest has multiple optional tasks ## [2101.1.1] From c88c3b0bee218e87f00492f1af1293c384d0f663 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Wed, 18 Dec 2024 12:18:12 +0000 Subject: [PATCH 07/24] 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 | 137 ++++++++++-------- .../command/QuestObjectArgument.java | 27 ++-- 2 files changed, 87 insertions(+), 77 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 d22d987b..7a2a0475 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.networking.NetworkManager; import dev.architectury.registry.registries.RegistrarManager; @@ -42,12 +43,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 @@ -79,20 +83,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")); }) ) ) @@ -100,22 +102,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); }) ) ) @@ -155,8 +149,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) @@ -171,18 +165,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) { - NetworkManager.sendToPlayer(player, OpenQuestBookMessage.lastOpenedQuest()); - } else if (qob instanceof QuestObject quest) { - if (canSeeQuestObject(player, quest)) { - NetworkManager.sendToPlayer(player, new OpenQuestBookMessage(quest.id)); + 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; } @@ -192,34 +199,47 @@ private static boolean canSeeQuestObject(ServerPlayer player, QuestObject qo) { }).orElse(false); } - 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) { + NetworkManager.sendToPlayer(player, OpenQuestBookMessage.lastOpenedQuest()); + return Command.SINGLE_SUCCESS; + } else { + if (getQuestObjectForString(qobId) instanceof Quest quest && playerCanSeeQuestObject(player, quest)) { + NetworkManager.sendToPlayer(player, new OpenQuestBookMessage(quest.id)); + 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 { @@ -253,7 +273,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) { @@ -268,7 +288,7 @@ private static int editingMode(CommandSourceStack source, ServerPlayer player, @ source.sendSuccess(() -> Component.translatable("commands.ftbquests.editing_mode.disabled", player.getDisplayName()), true); } - return 1; + return Command.SINGLE_SUCCESS; }).orElse(0); } @@ -284,11 +304,12 @@ private static int locked(CommandSourceStack source, ServerPlayer player, @Nulla source.sendSuccess(() -> Component.translatable("commands.ftbquests.locked.disabled", player.getDisplayName()), true); } - return 1; + return Command.SINGLE_SUCCESS; }).orElse(0); } - 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) { ServerQuestFile.INSTANCE.getTeamData(player).ifPresent(data -> { ProgressChange progressChange = new ProgressChange(questObject, player.getUUID()).setReset(reset); @@ -305,7 +326,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) { @@ -329,7 +350,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; @@ -370,7 +391,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<>(); @@ -379,14 +400,10 @@ private static int doReload(CommandSourceStack source) { ServerQuestFile instance = ServerQuestFile.INSTANCE; ServerPlayer sender = source.getPlayer(); - Optional playerData = instance.getTeamData(sender); - if (playerData.isEmpty()) { - return 0; - } - - if (sender != null && !playerData.get().getCanEdit(sender)) { + boolean canEdit = sender == null || instance.getTeamData(sender).map(data -> data.getCanEdit(sender)).orElse(false); + if (!canEdit) { source.sendFailure(Component.translatable("commands.ftbquests.command.error.not_editing")); - return 1; + return 0; } instance.load(); @@ -403,7 +420,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) { @@ -413,7 +430,7 @@ private static int toggleRewardBlocking(CommandSourceStack source, ServerPlayer source.sendSuccess(() -> Component.translatable("commands.ftbquests.command.feedback.rewards_blocked", data, data.areRewardsBlocked()), false); - return 1; + return Command.SINGLE_SUCCESS; }).orElse(0); } @@ -421,6 +438,6 @@ private static int toggleRewardBlocking(CommandSourceStack source, ServerPlayer 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 From 448565e896d69a08111e0e25cebd38aab1f1065c Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 2 Jan 2025 12:13:01 +0000 Subject: [PATCH 08/24] chore: only write reward feedback message if not empty --- .../dev/ftb/mods/ftbquests/quest/reward/CommandReward.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/CommandReward.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/CommandReward.java index ce9406cd..7f45fa9a 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/CommandReward.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/CommandReward.java @@ -2,7 +2,6 @@ import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftblibrary.config.ConfigGroup; -import dev.ftb.mods.ftblibrary.icon.Color4I; import dev.ftb.mods.ftblibrary.icon.Icon; import dev.ftb.mods.ftbquests.net.DisplayRewardToastMessage; import dev.ftb.mods.ftbquests.quest.Quest; @@ -55,7 +54,9 @@ public void writeData(CompoundTag nbt, HolderLookup.Provider provider) { nbt.putBoolean("elevate_perms", true); } if (silent) nbt.putBoolean("silent", true); - nbt.putString("feedback_message", feedbackMessage); + if (!feedbackMessage.isEmpty()) { + nbt.putString("feedback_message", feedbackMessage); + } } @Override From 6b078bdb12af875b92347e74647312d8bacee39d Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 2 Jan 2025 12:13:22 +0000 Subject: [PATCH 09/24] build: bump ftb library dep version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e4b3df63..65e33b59 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ fabric_api_version=0.100.8+1.21 fabric_api_version_range=>=0.100.1+1.21 architectury_api_version=13.0.6 -ftb_library_version=2101.1.4 +ftb_library_version=2101.1.7-SNAPSHOT ftb_teams_version=2101.1.0 # Optional deps From 15ab70fbb23c8d358d12182f05075f369802b432 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 3 Jan 2025 10:21:20 +0000 Subject: [PATCH 10/24] chore: changelog updated [ciskip] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eb98604..a1dfe2b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed a rare server-side crash which can occur when new (as in, never on this server before) players join the server * Timing issue with FTB Teams initialising their team data for the first time * Fixed quest completion toasts appearing more than once if a quest has multiple optional tasks +* Image objects with no click action can now be clicked-through, allowing the background to be scrolled/panned +* Fixed issue with several ftbquests subcommands meaning they could not be used in MC functions + * Commands are `/ftbquests change_progress`, `/ftbquests open_book`, and `/ftbquests export_reward_table_to_chest` ## [2101.1.1] From edc82d827b91078de2d4ad7e08b28140ae99d70e Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Mon, 6 Jan 2025 10:10:23 +0000 Subject: [PATCH 11/24] fix: include lang/ translation data in "Save Locally" Caveat: only saves translations this client knows about (i.e. that the server has sync'd to it). Saving every known translation is planned for a future version. --- .../ftbquests/client/gui/quests/OtherButtonsPanelBottom.java | 2 ++ .../main/java/dev/ftb/mods/ftbquests/quest/ServerQuestFile.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java index e154f917..dbffd5d9 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java @@ -154,6 +154,8 @@ private void saveLocally() { appendNum(fileName, time.get(Calendar.SECOND), '\0'); File file = new File(Minecraft.getInstance().gameDirectory, fileName.toString()).getCanonicalFile(); ClientQuestFile.INSTANCE.writeDataFull(file.toPath(), ClientQuestFile.INSTANCE.holderLookup()); + ClientQuestFile.INSTANCE.getTranslationManager().saveToNBT(file.toPath().resolve("lang"), true); + Component component = Component.translatable("ftbquests.gui.saved_as_file", "." + file.getPath().replace(Minecraft.getInstance().gameDirectory.getCanonicalFile().getAbsolutePath(), "")); component.getStyle().withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, file.getAbsolutePath())); Minecraft.getInstance().player.sendSystemMessage(component); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/ServerQuestFile.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/ServerQuestFile.java index ab20e401..0ab4a713 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/ServerQuestFile.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/ServerQuestFile.java @@ -149,7 +149,7 @@ public void saveNow() { shouldSave = false; } - getTranslationManager().saveToNBT(getFolder().resolve("lang")); + getTranslationManager().saveToNBT(getFolder().resolve("lang"), false); getAllTeamData().forEach(TeamData::saveIfChanged); } From 1bd1f42c8dc202c106e0246dbc30b0de6e55a278 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Mon, 6 Jan 2025 10:31:43 +0000 Subject: [PATCH 12/24] feat: new "All Table" reward type Rewards the player with one of every entry from the given reward table, regardless of weighting. https://github.com/FTBTeam/FTB-Mods-Issues/issues/32 --- .../quest/reward/AllTableReward.java | 28 +++++++++++++++++++ .../ftbquests/quest/reward/RewardTypes.java | 1 + .../assets/ftbquests/lang/en_us.json | 1 + 3 files changed, 30 insertions(+) create mode 100644 common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/AllTableReward.java diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/AllTableReward.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/AllTableReward.java new file mode 100644 index 00000000..8d9d03c3 --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/AllTableReward.java @@ -0,0 +1,28 @@ +package dev.ftb.mods.ftbquests.quest.reward; + +import dev.ftb.mods.ftbquests.quest.Quest; +import dev.ftb.mods.ftbquests.quest.loot.RewardTable; +import dev.ftb.mods.ftbquests.quest.loot.WeightedReward; +import net.minecraft.server.level.ServerPlayer; + +public class AllTableReward extends LootReward { + public AllTableReward(long id, Quest parent) { + super(id, parent); + } + + @Override + public RewardType getType() { + return RewardTypes.ALL_TABLE; + } + + @Override + public void claim(ServerPlayer player, boolean notify) { + RewardTable table = getTable(); + + if (table != null) { + for (WeightedReward wr : table.getWeightedRewards()) { + wr.getReward().claim(player, notify); + } + } + } +} diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/RewardTypes.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/RewardTypes.java index 1aa603b0..2013f464 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/RewardTypes.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/RewardTypes.java @@ -18,6 +18,7 @@ static RewardType register(ResourceLocation name, RewardType.Provider p, Supplie RewardType ITEM = register(FTBQuestsAPI.rl("item"), ItemReward::new, () -> Icon.getIcon("minecraft:item/diamond")); RewardType CHOICE = register(FTBQuestsAPI.rl("choice"), ChoiceReward::new, () -> Icons.COLOR_RGB).setExcludeFromListRewards(true); + RewardType ALL_TABLE = register(FTBQuestsAPI.rl("all_table"), AllTableReward::new, () -> Icons.COLOR_HSB).setExcludeFromListRewards(true); RewardType RANDOM = register(FTBQuestsAPI.rl("random"), RandomReward::new, () -> Icons.DICE).setExcludeFromListRewards(true); RewardType LOOT = register(FTBQuestsAPI.rl("loot"), LootReward::new, () -> Icons.MONEY_BAG).setExcludeFromListRewards(true); RewardType COMMAND = register(FTBQuestsAPI.rl("command"), CommandReward::new, () -> Icon.getIcon("minecraft:block/command_block_back")); diff --git a/common/src/main/resources/assets/ftbquests/lang/en_us.json b/common/src/main/resources/assets/ftbquests/lang/en_us.json index f878b772..c4b01402 100644 --- a/common/src/main/resources/assets/ftbquests/lang/en_us.json +++ b/common/src/main/resources/assets/ftbquests/lang/en_us.json @@ -437,6 +437,7 @@ "ftbquests.reward.ftbquests.item.count": "Count", "ftbquests.reward.ftbquests.item.only_one": "Only One", "ftbquests.reward.ftbquests.item.only_one.tooltip": "If inventory has that item, doesn't give it. Ignores NBT.", + "ftbquests.reward.ftbquests.all_table": "All Table Reward", "ftbquests.reward.ftbquests.choice": "Choice Reward", "ftbquests.reward.ftbquests.random": "Random Reward", "ftbquests.reward.ftbquests.loot": "Loot Reward", From e5c6dcf21351b2c7454d218c3f02c7a12de2cac6 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Mon, 6 Jan 2025 13:38:45 +0000 Subject: [PATCH 13/24] chore: update URL for "open wiki" link New site: https://docs.feed-the-beast.com/docs/mods/suite/Quests/ --- .../ftbquests/client/gui/quests/OtherButtonsPanelBottom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java index dbffd5d9..0eeae802 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java @@ -129,7 +129,7 @@ public void onClicked(MouseButton button) { contextMenu.add(new ContextMenuItem(Component.translatable("ftbquests.gui.reload_theme"), ThemeProperties.RELOAD_ICON.get(), b -> reload_theme())); contextMenu.add(new ContextMenuItem(Component.translatable("ftbquests.gui.wiki"), Icons.INFO, - b -> handleClick("https://help.ftb.team/mods"))); + b -> handleClick("https://docs.feed-the-beast.com/docs/mods/suite/Quests/"))); questScreen.openContextMenu(contextMenu); } From 98b385e3982802748d3668011676c70bc3f32af8 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Tue, 7 Jan 2025 08:30:17 +0000 Subject: [PATCH 14/24] feat: add new "none" quest shape and lock icon Lock icon controllable via both a quest file setting and player preferences Enabled by default https://github.com/FTBTeam/FTB-Mods-Issues/issues/1448 --- .../client/FTBQuestsClientConfig.java | 1 + .../client/gui/quests/QuestButton.java | 25 +++++++++++++++--- .../mods/ftbquests/quest/BaseQuestFile.java | 10 +++++++ .../ftb/mods/ftbquests/quest/QuestShape.java | 6 +++++ .../ftbquests/quest/theme/ThemeLoader.java | 2 ++ .../quest/theme/property/ThemeProperties.java | 1 + .../quest/translation/TranslationManager.java | 4 +-- .../assets/ftbquests/ftb_quests_theme.txt | 1 + .../assets/ftbquests/lang/en_us.json | 3 +++ .../ftbquests/textures/gui/quest_locked.png | Bin 0 -> 4932 bytes .../textures/shapes/none/background.png | Bin 0 -> 4270 bytes .../textures/shapes/none/outline.png | Bin 0 -> 4270 bytes .../ftbquests/textures/shapes/none/shape.png | Bin 0 -> 2015 bytes .../textures/shapes/none/shape.png.mcmeta | 5 ++++ 14 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 common/src/main/resources/assets/ftbquests/textures/gui/quest_locked.png create mode 100644 common/src/main/resources/assets/ftbquests/textures/shapes/none/background.png create mode 100644 common/src/main/resources/assets/ftbquests/textures/shapes/none/outline.png create mode 100644 common/src/main/resources/assets/ftbquests/textures/shapes/none/shape.png create mode 100644 common/src/main/resources/assets/ftbquests/textures/shapes/none/shape.png.mcmeta diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/FTBQuestsClientConfig.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/FTBQuestsClientConfig.java index b67fc61c..d0d93c78 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/FTBQuestsClientConfig.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/FTBQuestsClientConfig.java @@ -23,6 +23,7 @@ public interface FTBQuestsClientConfig { EnumValue PINNED_QUESTS_POS = UI.addEnum("pinned_quests_pos", PanelPositioning.NAME_MAP, PanelPositioning.RIGHT); IntValue PINNED_QUESTS_INSET_X = UI.addInt("pinned_quests_inset_x", 2); IntValue PINNED_QUESTS_INSET_Y = UI.addInt("pinned_quests_inset_y", 2); + BooleanValue SHOW_LOCK_ICON = UI.addBoolean("show_lock_icon", true); SNBTConfig XLATE = CONFIG.addGroup("xlate", 1); StringValue EDITING_LOCALE = XLATE.add(new LocaleValue(XLATE,"editing_locale", "")); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestButton.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestButton.java index aeec77e8..8c860353 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestButton.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestButton.java @@ -12,6 +12,7 @@ import dev.ftb.mods.ftblibrary.ui.input.MouseButton; import dev.ftb.mods.ftblibrary.util.TooltipList; import dev.ftb.mods.ftblibrary.util.client.PositionedIngredient; +import dev.ftb.mods.ftbquests.client.FTBQuestsClientConfig; import dev.ftb.mods.ftbquests.client.gui.ContextMenuBuilder; import dev.ftb.mods.ftbquests.net.CreateObjectMessage; import dev.ftb.mods.ftbquests.net.DeleteObjectMessage; @@ -325,6 +326,7 @@ public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) Color4I outlineColor = ThemeProperties.QUEST_NOT_STARTED_COLOR.get(quest); Icon questIcon = Color4I.empty() ; Icon hiddenIcon = Color4I.empty(); + Icon lockIcon = Color4I.empty(); TeamData teamData = questScreen.file.selfTeamData; boolean isCompleted = teamData.isCompleted(quest); @@ -364,9 +366,11 @@ public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) QuestShape shape = QuestShape.get(getShape()); - shape.getShape().withColor(Color4I.DARK_GRAY).draw(graphics, x, y, w, h); - shape.getBackground().withColor(Color4I.WHITE.withAlpha(150)).draw(graphics, x, y, w, h); - shape.getOutline().withColor(outlineColor).draw(graphics, x, y, w, h); + if (shape.shouldDraw()) { + shape.getShape().withColor(Color4I.DARK_GRAY).draw(graphics, x, y, w, h); + shape.getBackground().withColor(Color4I.WHITE.withAlpha(150)).draw(graphics, x, y, w, h); + shape.getOutline().withColor(outlineColor).draw(graphics, x, y, w, h); + } PoseStack poseStack = graphics.pose(); @@ -392,7 +396,12 @@ public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) } if (!canStart || !teamData.areDependenciesComplete(quest)) { - shape.getShape().withColor(Color4I.BLACK.withAlpha(100)).draw(graphics, x, y, w, h); + if (shape.shouldDraw()) { + shape.getShape().withColor(Color4I.BLACK.withAlpha(100)).draw(graphics, x, y, w, h); + } + if (quest.getQuestFile().showLockIcons() && FTBQuestsClientConfig.SHOW_LOCK_ICON.get()) { + lockIcon = ThemeProperties.LOCK_ICON.get(); + } } if (isMouseOver()) { @@ -414,6 +423,14 @@ public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) hiddenIcon.draw(graphics, 0, 0, s, s); poseStack.popPose(); } + + if (!lockIcon.isEmpty()) { + int s = (int) (w / 8F * 3F); + poseStack.pushPose(); + poseStack.translate(x + w - s, y + h - 1 - s, QuestScreen.Z_LEVEL); + lockIcon.draw(graphics, 0, 0, s, s); + poseStack.popPose(); + } } protected String getShape() { diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java index d06713b0..b173fc08 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java @@ -101,6 +101,7 @@ public void encode(RegistryFriendlyByteBuf buf, BaseQuestFile file) { protected String lockMessage; private ProgressionMode progressionMode; private int detectionDelay; + private boolean showLockIcons; private List allTasks; private List submitTasks; @@ -423,6 +424,7 @@ public final void writeData(CompoundTag nbt, HolderLookup.Provider provider) { nbt.putString("lock_message", lockMessage); nbt.putString("progression_mode", progressionMode.getId()); nbt.putInt("detection_delay", detectionDelay); + nbt.putBoolean("show_lock_icons", showLockIcons); } @Override @@ -460,6 +462,7 @@ public final void readData(CompoundTag nbt, HolderLookup.Provider provider) { if (nbt.contains("detection_delay")) { detectionDelay = nbt.getInt("detection_delay"); } + showLockIcons = !nbt.contains("show_lock_icons") || nbt.getBoolean("show_lock_icons"); } public final void writeDataFull(Path folder, HolderLookup.Provider provider) { @@ -831,6 +834,7 @@ public final void writeNetData(RegistryFriendlyByteBuf buffer) { buffer.writeUtf(lockMessage, Short.MAX_VALUE); ProgressionMode.NAME_MAP_NO_DEFAULT.write(buffer, progressionMode); buffer.writeVarInt(detectionDelay); + buffer.writeBoolean(showLockIcons); } @Override @@ -853,6 +857,7 @@ public final void readNetData(RegistryFriendlyByteBuf buffer) { lockMessage = buffer.readUtf(Short.MAX_VALUE); progressionMode = ProgressionMode.NAME_MAP_NO_DEFAULT.read(buffer); detectionDelay = buffer.readVarInt(); + showLockIcons = buffer.readBoolean(); } public final void writeNetDataFull(RegistryFriendlyByteBuf buffer) { @@ -1125,6 +1130,7 @@ public void fillConfigGroup(ConfigGroup config) { config.addEnum("progression_mode", progressionMode, v -> progressionMode = v, ProgressionMode.NAME_MAP_NO_DEFAULT); config.addInt("detection_delay", detectionDelay, v -> detectionDelay = v, 20, 0, 200); config.addBool("pause_game", pauseGame, v -> pauseGame = v, false); + config.addBool("show_lock_icons", showLockIcons, v -> showLockIcons = v, true).setNameKey("ftbquests.ui.show_lock_icon"); ConfigGroup defaultsGroup = config.getOrCreateSubgroup("defaults"); defaultsGroup.addBool("reward_team", defaultPerTeamReward, v -> defaultPerTeamReward = v, false); @@ -1407,6 +1413,10 @@ public RewardAutoClaim getDefaultRewardAutoClaim() { return defaultRewardAutoClaim; } + public boolean showLockIcons() { + return showLockIcons; + } + public List getEmergencyItems() { return Collections.unmodifiableList(emergencyItems); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestShape.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestShape.java index bd287000..8d466db9 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestShape.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/QuestShape.java @@ -27,6 +27,7 @@ public final class QuestShape extends Icon { private final String id; private final ImageIcon background, outline, shape; + private final boolean shouldDraw; private PixelBuffer shapePixels; public QuestShape(String id) { @@ -34,6 +35,7 @@ public QuestShape(String id) { background = new ImageIcon(FTBQuestsAPI.rl("textures/shapes/" + this.id + "/background.png")); outline = new ImageIcon(FTBQuestsAPI.rl("textures/shapes/" + this.id + "/outline.png")); shape = new ImageIcon(FTBQuestsAPI.rl("textures/shapes/" + this.id + "/shape.png")); + shouldDraw = !id.equals("none"); } public static void reload(List list) { @@ -99,6 +101,10 @@ public PixelBuffer getShapePixels() { return shapePixels; } + public boolean shouldDraw() { + return shouldDraw; + } + public static Map map() { return Collections.unmodifiableMap(MAP); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/ThemeLoader.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/ThemeLoader.java index ad293daa..fdf0e0ef 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/ThemeLoader.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/ThemeLoader.java @@ -89,6 +89,8 @@ public static void loadTheme(ResourceManager resourceManager) { list.add(s.trim()); } + list.add("none"); + QuestShape.reload(new ArrayList<>(list)); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/property/ThemeProperties.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/property/ThemeProperties.java index d6d16198..fd4ecacd 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/property/ThemeProperties.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/theme/property/ThemeProperties.java @@ -123,6 +123,7 @@ public boolean equals(Object o) { IconProperty EDIT_ICON = new IconProperty("edit_icon"); IconProperty MOVE_UP_ICON = new IconProperty("move_up_icon"); IconProperty MOVE_DOWN_ICON = new IconProperty("move_down_icon"); + IconProperty LOCK_ICON = new IconProperty("lock_icon"); // Quest window // IconProperty ICON = new IconProperty("icon"); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/translation/TranslationManager.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/translation/TranslationManager.java index 082d0554..6744f919 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/translation/TranslationManager.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/translation/TranslationManager.java @@ -69,9 +69,9 @@ public void loadFromNBT(Path langFolder) { } } - public void saveToNBT(Path langFolder) { + public void saveToNBT(Path langFolder, boolean force) { map.forEach((locale, table) -> { - if (table.isSaveNeeded()) { + if (force || table.isSaveNeeded()) { boolean prevSort = SNBT.setShouldSortKeysOnWrite(true); Path savePath = langFolder.resolve(locale + ".snbt"); if (!SNBT.write(savePath, table.saveToNBT())) { diff --git a/common/src/main/resources/assets/ftbquests/ftb_quests_theme.txt b/common/src/main/resources/assets/ftbquests/ftb_quests_theme.txt index 05f809f4..1fbd8270 100644 --- a/common/src/main/resources/assets/ftbquests/ftb_quests_theme.txt +++ b/common/src/main/resources/assets/ftbquests/ftb_quests_theme.txt @@ -44,6 +44,7 @@ modpack_icon: ftbquests:textures/item/book.png reward_table_icon: ftblibrary:icons/money_bag shop_icon: ftbquests:textures/gui/shop.png; color=#EF9E1A collect_rewards_icon: ftbquests:textures/gui/collect_rewards.png; color=#EF9E1A +lock_icon: ftbquests:textures/gui/quest_locked.png delete_icon: ftblibrary:icons/remove reload_icon: ftblibrary:icons/refresh download_icon: ftblibrary:icons/down diff --git a/common/src/main/resources/assets/ftbquests/lang/en_us.json b/common/src/main/resources/assets/ftbquests/lang/en_us.json index c4b01402..ad45a240 100644 --- a/common/src/main/resources/assets/ftbquests/lang/en_us.json +++ b/common/src/main/resources/assets/ftbquests/lang/en_us.json @@ -188,6 +188,7 @@ "ftbquests.quest.shape.octagon": "Octagon", "ftbquests.quest.shape.heart": "Heart", "ftbquests.quest.shape.gear": "Gear", + "ftbquests.quest.shape.none": "", "ftbquests.quest.misc.can_repeat": "Repeatable Quest", "ftbquests.quest.tasks_ignore_dependencies": "Tasks Ignore Dependencies", "ftbquests.quest.tasks_ignore_dependencies.tooltip": "Allow to start tasks without completing dependencies", @@ -543,6 +544,8 @@ "ftbquests.ui.pinned_quests_inset_x.tooltip": "Inset is toward center of screen.\nIgnored if X pos is center.", "ftbquests.ui.pinned_quests_inset_y": "Pinned Quests Panel Y Inset", "ftbquests.ui.pinned_quests_inset_y.tooltip": "Inset is toward center of screen.\nIgnored if Y pos is center.", + "ftbquests.ui.show_lock_icon": "Show Icon for Locked Quests", + "ftbquests.ui.show_lock_icon.tooltip": "Show a padlock icon on any quests which can't yet be started due to dependency restrictions.", "ftbquests.xlate": "Translations", "ftbquests.xlate.editing_locale": "Locale Override", "ftbquests.xlate.editing_locale.tooltip": "The language used for editing FTB Quests text: title text for chapters, quests, tasks and reward tables, subtitles for chapters and quests, and quest description text.\nIf empty, use the current Minecraft language setting.", diff --git a/common/src/main/resources/assets/ftbquests/textures/gui/quest_locked.png b/common/src/main/resources/assets/ftbquests/textures/gui/quest_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..8fa948bf5294a05edd0b6c1008ce8f8fea55b6dc GIT binary patch literal 4932 zcmeHKeNYtV8DGT!Jq6L)KohO&N++c0-tF$)?%u5&gl{f#aD`JoEG2up%dvsGJ#H87 zfNBUz8x3Jn8mEz9G}g4ii4J2+XAoN};z!aXwoTAUFi4!XPE3t8q(TI1-@Sta8g0Y0 z_Al;c?{1&>d4BKn{NCU5zPH@&s%>rYvF{wvM0e(Q_FykmY+c5C;ypWFdFfDi-S%65+SPt)qByIlqpRl) zdTm1{x9PzReHXX1TtYt4A+Zf{1DK;dYROjW*nK4jFC6Py)zw$NKli&o7!^K!asBnH zSKoUkN5KfA{^RdMqi43ixOnAj?{28ICY^JvxSaZrs~@G`T=@Rw;;-#HGgbmY3$}@A zX*Nq*+Eg{5hK|iYGH0K#t!U2M)Q#+Hx_?>q8Qbo^?n7F;H5sqQJkh*yRnGTrMpz$c zTM?UeynA@~tYhH#$>PF@!jzba1=X<~D@ynGkGG{h6Q@fYI$bxqa;SId-{^1UkiC~0 z4-{uIyJ8OaY*;g{EEEqg(m0b4+`jk(iUgk=&j;gj}7sSd2p-#gRZCP zr=1kKdnBoP+s^pru?~OV-o?w4y`{CEKP&%%O+MQax9hXS>TN{{wOtpEzsA4X|4#pR zG6oVZ?x`vM@aW0ngYOo7{L)18%SV58_|2MwW-good zg`V!Wt8YSl#m3GDhlgY0bon=qobGK{clm~tSGgs5-4gF}%BrjeXgCW zujEWTlANST^fLg!B`7THcU8G%#&1RfxD5EMnso>qa8WAFNRibBr%7G`HfRl649)P1 zwFHu+ffK!aIg_8BISB#o%t(czco?0|=ksZOdadMj>Tr|Eq{9duL7>0`mFwIJ>qp)4 zS`{LQkuJ!bSM(^NW&{D_@Kk;-kJTE2cgvG306uhn)}zCK6lno$ z;AOQk=@M6?)@4aIBfz5NMV`?cj0Rf3^(f~MDAYk1Fw|tyn^009Ngk(Zj$`Q=5Ei$r zux?J6f)G^*Oiyt}jzje{rAG}UhodZQ!cmgr2tscpXx?a?0g>kwfsEOz|0)}vW6CA3 ziv{a1x>%>6^SGUXIn)(mQfwA8LTIsRi>-=P)BrP*Bf4w+(*t(VB@`&E$|6qdDNr$n z(-ck+1U2onQSiz@x+*4)Y4yR8&=N9fQkCSjO1xl3^noCCr5I2r;F?u653mDvKsuA= z6<9?`2N!_D2m^{^C}y(bI765igZ>c=XRu)6A#cfRm!v8)qSgkhDTKhRppki2VMA~v zqsCKqoMZ?+qo*Fhu$gcmNwK`{KY6Qd3nvCOlP$`MZw^d@GiD5X< zjtt8M3+4i4@_|eNT5wRjlX||a07EFURId0~uaM#d{F;#zb+?3VQ~d@Lq!uUOghJ8H zOFsGG*%|mM1a%qj%De7MlqR#a!oL*8Rj_WS0Jh}GJ>cIJ%;F@487Ug{nUO*%+Q6YK zMUf~;>2VWH8wi#Pw#z*#m|$=tLz0t1`#%&+Q${_{(?*o!DI7KM4i+_%5Gh4|gj#3v99h-NOli?!DWZk|>6w)8eNm#XnhMqE0<|rqr9L`+~lM4l|A_poO3f z9D(3O)rF4KNs)kj@8xT4$Q`4gU7|z$159Kh_Bz*TWs_v(lm4>U;06fBs`&`HCmIScag*UsbOKPrzJhbZv5TJw&duo@XF@oXBQVXuJcDO1^xDB%^YnHy-5qp#gEH_FkCuE7J( z-5A{CkBmFL3|h131U8YIQdpU^p=06x=b@tWe{XAwkbQLjAI(Qf=rS E1SIFhTmS$7 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/ftbquests/textures/shapes/none/background.png b/common/src/main/resources/assets/ftbquests/textures/shapes/none/background.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab95c9b7a57d33b034fc90c8c6b431aafa9b46a GIT binary patch literal 4270 zcmeHKd2ka|7~iP05DXMVLA-Xh!l1C(BiS5l(^{HBGmtj4wKNLC%kE3MG|8sBn>H=t z0aOH(v*U~oM^&un;DA$5igd74aCGW{4C2|r@sM?~6 zzV_xfV>@@T3FpSC(=GK)kG#0+%#veiKgfln&x|NL8~lC|KduGCOe>|lynrt+uQwr- z#+GFpJq2xn3H3!K&BZs5sm_=;vV7}zj}z;f&G}DfEU7QMZtk7u)BTe-T{Us`!RF4+ zw?fAbwg=~@&(F=cV5*w9Wx~Q8hko5ObL}K5>yyK^UDtlnGB#r}Yzi)9Zaw;3_~vmF zZ6|JH&X{kg%Q!S`mKeTT-PEu$vh3p$=dpzik=m}`g3Fe4gzhc6aUIk4{CGLzt!SC0 zd8@?tpyj3B-=lCc8pjxV%yb@qtDcyq<58nwfM;u!FS$!;HlM_&o-4D z*iaSxe1Cgz`tkB_w_T`zY|DY&ZzgYVD_A^k&IWvDu4PKYy!?jhtrJGy#IIla^QvoG zz1I2#Q)p#I<0Y2epQT@1@#%}rKa^~a7f&0bwZ4Du@T83QM?9MR@b~OJr@k1~c~9ET zANRDri-mK1FYIkTGVbkG%Yp@aIXL#Ek4$OXFF(<6-Rnhjj(mAvm+gGh3_3CTvg04U z+}u%h9@`<6y?#w+XT~HdaBkn>mZi6Rc}^~>ygO$}P^HNKR@VfhwPVlirss~U(4P=`X`Rte#7f=dt>2+0u3 zg-AQix=6Px>?BzSaJXEojRi0~0HR2dP*;NJz^HU65kWca4Ci*(?W6$gF4FGgSTe-9 zA<5YV4zf1R0m3c=N)-56vJwNRoKg%#AeD$m424cOpBwOb2-ZsXO9D}#g^`1Ym@CDT zseVPN6oVxi(Ai|1HV4|?X?M8oPN&=5Z&U^q6?LMH${I*EaT0+Byg-8plx8`eW_a3B%Fw)xHa=H583F5mvWKr~vT@ zMAM_EU3=xy-?RdN9OGnJniOc81J%zRCS49EO>)4-G9lCy;Lqr)9M)=p0&^pXN5mCs zPlGFbno+2m2FFrU0re^%!bqAS`w4UO5JvSROzAVDH{&eoFPvl<1_Oo+^6QqN;e}=) z)i(@#IMc_@|9JJ(;(y!$K@V>-Bz}kK8m4PV3=GM5xVwhw8WICTG9K=(|BNnk|LYWt zqklm)=w)gB#dj>|MJp{dXSNr+sDBz?d1^C~j7k&)RSX+*Q2&}<+E{rC3DY&7KR^9z zQ<^!`y5=tFcO)wFd2>rsADlYKn9%Cg#-EB}m*3Ec9{H=t z0aOH(v*U~oM^&un;DA$5igd74aCGW{4C2|r@sM?~6 zzV_xfV>@@T3FpSC(=GK)kG#0+%#veiKgfln&x|NL8~lC|KduGCOe>|lynrt+uQwr- z#+GFpJq2xn3H3!K&BZs5sm_=;vV7}zj}z;f&G}DfEU7QMZtk7u)BTe-T{Us`!RF4+ zw?fAbwg=~@&(F=cV5*w9Wx~Q8hko5ObL}K5>yyK^UDtlnGB#r}Yzi)9Zaw;3_~vmF zZ6|JH&X{kg%Q!S`mKeTT-PEu$vh3p$=dpzik=m}`g3Fe4gzhc6aUIk4{CGLzt!SC0 zd8@?tpyj3B-=lCc8pjxV%yb@qtDcyq<58nwfM;u!FS$!;HlM_&o-4D z*iaSxe1Cgz`tkB_w_T`zY|DY&ZzgYVD_A^k&IWvDu4PKYy!?jhtrJGy#IIla^QvoG zz1I2#Q)p#I<0Y2epQT@1@#%}rKa^~a7f&0bwZ4Du@T83QM?9MR@b~OJr@k1~c~9ET zANRDri-mK1FYIkTGVbkG%Yp@aIXL#Ek4$OXFF(<6-Rnhjj(mAvm+gGh3_3CTvg04U z+}u%h9@`<6y?#w+XT~HdaBkn>mZi6Rc}^~>ygO$}P^HNKR@VfhwPVlirss~U(4P=`X`Rte#7f=dt>2+0u3 zg-AQix=6Px>?BzSaJXEojRi0~0HR2dP*;NJz^HU65kWca4Ci*(?W6$gF4FGgSTe-9 zA<5YV4zf1R0m3c=N)-56vJwNRoKg%#AeD$m424cOpBwOb2-ZsXO9D}#g^`1Ym@CDT zseVPN6oVxi(Ai|1HV4|?X?M8oPN&=5Z&U^q6?LMH${I*EaT0+Byg-8plx8`eW_a3B%Fw)xHa=H583F5mvWKr~vT@ zMAM_EU3=xy-?RdN9OGnJniOc81J%zRCS49EO>)4-G9lCy;Lqr)9M)=p0&^pXN5mCs zPlGFbno+2m2FFrU0re^%!bqAS`w4UO5JvSROzAVDH{&eoFPvl<1_Oo+^6QqN;e}=) z)i(@#IMc_@|9JJ(;(y!$K@V>-Bz}kK8m4PV3=GM5xVwhw8WICTG9K=(|BNnk|LYWt zqklm)=w)gB#dj>|MJp{dXSNr+sDBz?d1^C~j7k&)RSX+*Q2&}<+E{rC3DY&7KR^9z zQ<^!`y5=tFcO)wFd2>rsADlYKn9%Cg#-EB}m*3Ec9{N2bZe?^J zG%hhNG!sB*!~g&XT}ebiRCr$Povp7OMHq)G5ET{33Pc476^MpZR6tT$fv7-MR3IuU zDl3~*R3a-HLPAAD5F{WF2>u92T7sYilF|TzKm+CL@;;lrhuzuV?%D4%JM%utliTz5 z-gEcvJ7;#jcJ{>4(NXH2G|!nAORtz$&1>eZQvOYh|2zIZeuke_@0pA;xKT;g>n6#3 z$Na?n#+(Z8!F%zZy!Qn&nSvpM8}S8`Pb#0_&&}V>wfMv2ck;VA298A?WAw^k#J*_0 zZGLM0YR1BECdb6Fag6HNqf`bXlTX*1=7;8*pK|!O{5k|`2aff!8U3X~1|#uhlk4t} zW^D8=7(bbJP5u`3Hy&IWJg}Us_sqY{RQTI`VA8WtfAhhV!2`+3q3Yav;`h}KADPh< z1Q}e}mrOn#xj|t+W2$qqnliYOH12MgF}3E`h6dwJ^I+Xb8C*dIY`!paqwVtWmB~3# z=W0=9a6wO*_e@8_x#1kCbGL{xxR9@!IezNfXlv{~GFNLP%HRSrhQ9V7&Q?&Dh3DpV zQ=Q9MmBAUMI?J(zKNoZv7adxb!I?a5J~W;FsM>%_!eZS;8Jxkh=GUe(97YX8elR(w z>ReA!1}91b=85Ud%P;2aT}2t3AP1Q9z|PVz?aa)mI`^ZL!I66M*MUpKY=uAuM@Y5r z$zP7Jarn%f<#Hf{1Eq1|*Zk!P3&B%!a>Gyt2S``XTl?z~7J~0ha-ee4OBw7bgFN2P zU!SlLkb}|l2Qt`GAK{A!a-njxR~hUfWBX2AkQ0@wPGxY9%~ zdfk;IgF;sCI&l+T3T~S!mo;TDkahb$&c8iaa=!PLG#L#1z;w1j*el?UWtG#NGFb67 z)7dtivxYKQ@nh53HY_Yvxh<5z(pOBiKF!%4t@jzoVCnm&lLq%qmE+a|K$ znq2k>8i7)F=5W%2oHlaZ2$+oe#kehtGYz(@Y21chdr``YQzuOttBGqbN|{Y^(xfp{ zSbJeIm+Xr@(uLvS+IoR%@{x@WowOmxyPQ{(kBl8VX;YgYs3xBlqR;xJ5BC@DdIQx0 zz^2De9+2xs0bu8AClAPVqX4k|xswOvx>f*KKjz)Pw5cu`qdQov89XikEE;g~f+wHs z3V`40l^?~eWXA=-XaDk}m>oPW06Y}Q$qRD6D*$smUCY@PVv<96#2yY%aNdaKN zppz%(C;&bLoG%x>0OU>OATsH!x{>Yg081%>(d|*0h(E8{&87%e1 zqUkW|v2o~K!tL{!sd8H=gB86N*gm_7AEgWidM&UW!iM{9-oH&53_N4{@bLEFVUcG| zmD8OvD7F@7monJlDbp_n@jyeb3RQn?zJH%GxJSkYyjKt(6o&Jr%2}&2*u(3l6Bjp3m8(u= zu%|vi6c4SFJ^Pfwo^q}6_Q2tJWDdISSCzp5GROSHbk-qk@jo@?JbzzhaG(q~`VA$^ z6DFSnym4m^`-~<_h^qOEf zW=%fI;6%9`c*Agho|&^$`&DId25A_6XF9_Phj)S2{^SOqQU+)8wCUpn!}zECT7`c8 zq^@3hOWObWv-}$J ztvPsBZ>%!7pj2lpSa39)n?K{%p!44?Ym%edxL23L|ri?_`j7MHXo zo@+g;cY7`1TtXRKS-LnmZcfEb8++I7FRct7NJbD-SY)DF^;Ni6Z}t(rvC80qWyI^g zxutGnTNPi0!oA37-V|jp61g<6r+tju)$~a?{A{u_$6_OSQ0gEj8@DuWSAADN8~X*9+(?aWLa$HcL%+K->A3~oef1{zcBd`*jb&Dwzt z7x|t1ZjNEM_wLq|!Hr6FOHYD%2igui6lspZCfzhw1 x@~mVYm`yVI1S9_@#{V6EA3u{!qP0gS{sX0dhfnKd)eQgu002ovPDHLkV1l;W#peJ3 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/ftbquests/textures/shapes/none/shape.png.mcmeta b/common/src/main/resources/assets/ftbquests/textures/shapes/none/shape.png.mcmeta new file mode 100644 index 00000000..a39ce83c --- /dev/null +++ b/common/src/main/resources/assets/ftbquests/textures/shapes/none/shape.png.mcmeta @@ -0,0 +1,5 @@ +{ + "texture": { + "blur": true + } +} \ No newline at end of file From 55b34033798c9fca8d129c85e4018d963327a1c6 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Wed, 8 Jan 2025 08:45:59 +0000 Subject: [PATCH 15/24] fix: recipes/loot tables/tag naming for 1.21 --- .../{loot_tables => loot_table}/blocks/loot_crate_opener.json | 0 .../ftbquests/{loot_tables => loot_table}/blocks/screen_1.json | 0 .../ftbquests/{loot_tables => loot_table}/blocks/screen_3.json | 0 .../ftbquests/{loot_tables => loot_table}/blocks/screen_5.json | 0 .../ftbquests/{loot_tables => loot_table}/blocks/screen_7.json | 0 .../main/resources/data/ftbquests/{recipes => recipe}/book.json | 0 .../data/ftbquests/{recipes => recipe}/loot_crate_opener.json | 0 .../resources/data/ftbquests/{recipes => recipe}/screen_1.json | 0 .../resources/data/ftbquests/{recipes => recipe}/screen_3.json | 0 .../resources/data/ftbquests/{recipes => recipe}/screen_5.json | 0 .../resources/data/ftbquests/{recipes => recipe}/screen_7.json | 0 .../ftbquests/{recipes => recipe}/task_screen_configurator.json | 0 .../tags/{entity_types => entity_type}/no_loot_crates.json | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename common/src/main/resources/data/ftbquests/{loot_tables => loot_table}/blocks/loot_crate_opener.json (100%) rename common/src/main/resources/data/ftbquests/{loot_tables => loot_table}/blocks/screen_1.json (100%) rename common/src/main/resources/data/ftbquests/{loot_tables => loot_table}/blocks/screen_3.json (100%) rename common/src/main/resources/data/ftbquests/{loot_tables => loot_table}/blocks/screen_5.json (100%) rename common/src/main/resources/data/ftbquests/{loot_tables => loot_table}/blocks/screen_7.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/book.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/loot_crate_opener.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/screen_1.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/screen_3.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/screen_5.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/screen_7.json (100%) rename common/src/main/resources/data/ftbquests/{recipes => recipe}/task_screen_configurator.json (100%) rename common/src/main/resources/data/ftbquests/tags/{entity_types => entity_type}/no_loot_crates.json (100%) diff --git a/common/src/main/resources/data/ftbquests/loot_tables/blocks/loot_crate_opener.json b/common/src/main/resources/data/ftbquests/loot_table/blocks/loot_crate_opener.json similarity index 100% rename from common/src/main/resources/data/ftbquests/loot_tables/blocks/loot_crate_opener.json rename to common/src/main/resources/data/ftbquests/loot_table/blocks/loot_crate_opener.json diff --git a/common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_1.json b/common/src/main/resources/data/ftbquests/loot_table/blocks/screen_1.json similarity index 100% rename from common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_1.json rename to common/src/main/resources/data/ftbquests/loot_table/blocks/screen_1.json diff --git a/common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_3.json b/common/src/main/resources/data/ftbquests/loot_table/blocks/screen_3.json similarity index 100% rename from common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_3.json rename to common/src/main/resources/data/ftbquests/loot_table/blocks/screen_3.json diff --git a/common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_5.json b/common/src/main/resources/data/ftbquests/loot_table/blocks/screen_5.json similarity index 100% rename from common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_5.json rename to common/src/main/resources/data/ftbquests/loot_table/blocks/screen_5.json diff --git a/common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_7.json b/common/src/main/resources/data/ftbquests/loot_table/blocks/screen_7.json similarity index 100% rename from common/src/main/resources/data/ftbquests/loot_tables/blocks/screen_7.json rename to common/src/main/resources/data/ftbquests/loot_table/blocks/screen_7.json diff --git a/common/src/main/resources/data/ftbquests/recipes/book.json b/common/src/main/resources/data/ftbquests/recipe/book.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/book.json rename to common/src/main/resources/data/ftbquests/recipe/book.json diff --git a/common/src/main/resources/data/ftbquests/recipes/loot_crate_opener.json b/common/src/main/resources/data/ftbquests/recipe/loot_crate_opener.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/loot_crate_opener.json rename to common/src/main/resources/data/ftbquests/recipe/loot_crate_opener.json diff --git a/common/src/main/resources/data/ftbquests/recipes/screen_1.json b/common/src/main/resources/data/ftbquests/recipe/screen_1.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/screen_1.json rename to common/src/main/resources/data/ftbquests/recipe/screen_1.json diff --git a/common/src/main/resources/data/ftbquests/recipes/screen_3.json b/common/src/main/resources/data/ftbquests/recipe/screen_3.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/screen_3.json rename to common/src/main/resources/data/ftbquests/recipe/screen_3.json diff --git a/common/src/main/resources/data/ftbquests/recipes/screen_5.json b/common/src/main/resources/data/ftbquests/recipe/screen_5.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/screen_5.json rename to common/src/main/resources/data/ftbquests/recipe/screen_5.json diff --git a/common/src/main/resources/data/ftbquests/recipes/screen_7.json b/common/src/main/resources/data/ftbquests/recipe/screen_7.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/screen_7.json rename to common/src/main/resources/data/ftbquests/recipe/screen_7.json diff --git a/common/src/main/resources/data/ftbquests/recipes/task_screen_configurator.json b/common/src/main/resources/data/ftbquests/recipe/task_screen_configurator.json similarity index 100% rename from common/src/main/resources/data/ftbquests/recipes/task_screen_configurator.json rename to common/src/main/resources/data/ftbquests/recipe/task_screen_configurator.json diff --git a/common/src/main/resources/data/ftbquests/tags/entity_types/no_loot_crates.json b/common/src/main/resources/data/ftbquests/tags/entity_type/no_loot_crates.json similarity index 100% rename from common/src/main/resources/data/ftbquests/tags/entity_types/no_loot_crates.json rename to common/src/main/resources/data/ftbquests/tags/entity_type/no_loot_crates.json From 9ae01de4560506632301e648f2f87d2d746f81c7 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Mon, 6 Jan 2025 10:32:19 +0000 Subject: [PATCH 16/24] fix: rewards in reward tables must have unique IDs Necessary for translations to work for them in the new 1.21 translations system. Also a few other small fixes & improvements to the reward table editor in general. https://github.com/FTBTeam/FTB-Mods-Issues/issues/1442 --- .../mods/ftbquests/client/GuiProviders.java | 14 +++ .../client/gui/EditRewardTableScreen.java | 12 +-- .../client/gui/RewardTablesScreen.java | 5 + .../mods/ftbquests/net/EditObjectMessage.java | 2 +- .../mods/ftbquests/quest/BaseQuestFile.java | 91 +++++++++------- .../ftbquests/quest/loot/RewardTable.java | 102 ++++++++++++++---- .../ftbquests/quest/loot/WeightedReward.java | 7 ++ .../mods/ftbquests/quest/reward/Reward.java | 7 ++ .../ftbquests/quest/reward/StageReward.java | 7 +- 9 files changed, 184 insertions(+), 63 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/GuiProviders.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/GuiProviders.java index 8f8c75ad..abb624d4 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/GuiProviders.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/GuiProviders.java @@ -48,6 +48,7 @@ public static RewardType.GuiProvider defaultRewardGuiProvider(RewardType.Provide } gui.run(); }); + group.setNameKey(reward.getType().getTypeId().toLanguageKey("ftbquests.reward")); reward.fillConfigGroup(reward.createSubGroup(group)); new EditConfigScreen(group).openGui(); } @@ -160,6 +161,7 @@ private static void openSetupGui(Runnable gui, BiConsumer cal callback.accept(task, task.getType().makeExtraNBT()); } }); + group.setNameKey(task.getType().getTypeId().toLanguageKey("ftbquests.task")); task.fillConfigGroup(task.createSubGroup(group)); new EditConfigScreen(group).openGui(); @@ -206,5 +208,17 @@ public static void setRewardGuiProviders() { }, RewardTypes.XP_LEVELS.getDisplayName()).atMousePosition(); panel.getGui().pushModalPanel(overlay); }); + + RewardTypes.STAGE.setGuiProvider((panel, quest, callback) -> { + StringConfig c = new StringConfig(); + + EditStringConfigOverlay overlay = new EditStringConfigOverlay<>(panel.getGui(), c, accepted -> { + if (accepted) { + callback.accept(new StageReward(0L, quest, c.getValue())); + } + panel.run(); + }, RewardTypes.STAGE.getDisplayName()).atMousePosition(); + panel.getGui().pushModalPanel(overlay); + }); } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/EditRewardTableScreen.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/EditRewardTableScreen.java index 08cc2952..8d7b1388 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/EditRewardTableScreen.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/EditRewardTableScreen.java @@ -15,7 +15,6 @@ import dev.ftb.mods.ftblibrary.util.TooltipList; import dev.ftb.mods.ftblibrary.util.client.PositionedIngredient; import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; -import dev.ftb.mods.ftbquests.quest.QuestObjectBase; import dev.ftb.mods.ftbquests.quest.loot.RewardTable; import dev.ftb.mods.ftbquests.quest.loot.WeightedReward; import dev.ftb.mods.ftbquests.quest.reward.RewardType; @@ -24,6 +23,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Items; +import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.List; @@ -40,11 +40,7 @@ public class EditRewardTableScreen extends AbstractButtonListScreen { public EditRewardTableScreen(Runnable parentScreen, RewardTable originalTable, Consumer callback) { this.parentScreen = parentScreen; this.callback = callback; - - editedTable = QuestObjectBase.copy(originalTable, () -> new RewardTable(originalTable.id, originalTable.getFile())); - if (editedTable != null) { - editedTable.setRawTitle(originalTable.getRawTitle()); - } + this.editedTable = originalTable.copy(); setBorder(1, 1, 1); } @@ -204,6 +200,10 @@ private WeightedRewardButton(Panel panel, WeightedReward wr) { public void addMouseOverText(TooltipList list) { super.addMouseOverText(list); + if (isKeyDown(GLFW.GLFW_KEY_F1) || isShiftKeyDown() && isCtrlKeyDown()) { + list.add(Component.literal(wr.getReward().getCodeString()).withStyle(ChatFormatting.DARK_GRAY)); + } + if (getMouseX() > getX() + width - 13) { list.add(Component.translatable("gui.remove")); } else if (getMouseX() > getX() + width - 26) { diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/RewardTablesScreen.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/RewardTablesScreen.java index 378196e6..f5b77b87 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/RewardTablesScreen.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/RewardTablesScreen.java @@ -37,6 +37,7 @@ import net.minecraft.world.item.Items; import org.apache.commons.lang3.mutable.MutableInt; import org.jetbrains.annotations.NotNull; +import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.Comparator; @@ -304,6 +305,10 @@ private void toggleLootCrate() { public void addMouseOverText(TooltipList list) { super.addMouseOverText(list); + if (isKeyDown(GLFW.GLFW_KEY_F1) || isShiftKeyDown() && isCtrlKeyDown()) { + list.add(Component.literal(table.getCodeString()).withStyle(ChatFormatting.DARK_GRAY)); + } + if (getMouseX() > getX() + width - 13) { list.add(Component.translatable(pendingDeleteIndexes.contains(idx) ? "ftbquests.gui.restore" : "gui.remove")); } else if (getMouseX() > getX() + width - 26) { diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/EditObjectMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/EditObjectMessage.java index 76dccf7e..ed950178 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/EditObjectMessage.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/EditObjectMessage.java @@ -46,10 +46,10 @@ public static void handle(EditObjectMessage message, NetworkManager.PacketContex QuestObjectBase object = ServerQuestFile.INSTANCE.getBase(message.id); if (object != null) { object.readData(message.nbt, context.registryAccess()); - object.editedFromGUIOnServer(); ServerQuestFile.INSTANCE.clearCachedData(); ServerQuestFile.INSTANCE.markDirty(); NetworkHelper.sendToAll(context.getPlayer().getServer(), new EditObjectResponseMessage(object)); + object.editedFromGUIOnServer(); } } }); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java index b173fc08..7fbffb76 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/BaseQuestFile.java @@ -44,6 +44,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.mutable.MutableInt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -60,16 +61,16 @@ public abstract class BaseQuestFile extends QuestObject implements QuestFile { public static int VERSION = 13; public static final StreamCodec STREAM_CODEC = new StreamCodec<>() { - @Override - public BaseQuestFile decode(RegistryFriendlyByteBuf buf) { + @Override + public BaseQuestFile decode(RegistryFriendlyByteBuf buf) { return Util.make(FTBQuestsClient.createClientQuestFile(), file -> file.readNetDataFull(buf)); - } + } - @Override - public void encode(RegistryFriendlyByteBuf buf, BaseQuestFile file) { + @Override + public void encode(RegistryFriendlyByteBuf buf, BaseQuestFile file) { file.writeNetDataFull(buf); - } - }; + } + }; private final DefaultChapterGroup defaultChapterGroup; final List chapterGroups; @@ -267,6 +268,17 @@ public QuestObjectBase remove(long id) { return null; } + public T getQuestObjectOrThrow(long id, Class cls) { + QuestObjectBase object = getBase(id); + if (object == null) { + throw new IllegalArgumentException("Unknown object id " + id); + } else if (cls.isAssignableFrom(object.getClass())) { + return cls.cast(object); + } else { + throw new IllegalArgumentException("Wrong class for object id " + id + ": wanted " + cls.getName() + ", got " + object.getClass().getName()); + } + } + @Nullable public Chapter getChapter(long id) { QuestObjectBase object = getBase(id); @@ -341,7 +353,11 @@ public void refreshIDMap() { chapter.getQuestLinks().forEach(link -> questObjectMap.put(link.id, link)); }); - clearCachedData(); + refreshRewardTableRewardIDs(); + } + + public void refreshRewardTableRewardIDs() { + rewardTables.forEach(table -> table.getWeightedRewards().forEach(wr -> questObjectMap.put(wr.getReward().id, wr.getReward()))); } public QuestObjectBase create(long id, QuestObjectType type, long parent, CompoundTag extra) { @@ -375,15 +391,15 @@ public QuestObjectBase create(long id, QuestObjectType type, long parent, Compou throw new IllegalArgumentException("Parent quest not found!"); } case REWARD -> { - Quest quest = getQuest(parent); - if (quest != null) { - Reward reward = RewardType.createReward(id, quest, extra.getString("type")); - if (reward != null) { - return reward; - } - throw new IllegalArgumentException("Unknown reward type!"); + String rewardType = extra.getString("type"); + if (RewardTable.isFakeQuestId(parent)) { + return RewardTable.createRewardForTable(id, rewardType, this); + } else { + Quest quest = getQuestObjectOrThrow(parent, Quest.class); + Reward reward = RewardType.createReward(id, quest, rewardType); + Validate.isTrue(reward != null, "Unknown reward type: " + rewardType); + return reward; } - throw new IllegalArgumentException("Parent quest not found!"); } case REWARD_TABLE -> { return new RewardTable(id, this); @@ -723,6 +739,7 @@ public final void readDataFull(Path folder, HolderLookup.Provider provider) { rewardTables.sort(Comparator.comparingInt(c -> objectOrderMap.get(c.id))); updateLootCrates(); + refreshRewardTableRewardIDs(); /* for (Chapter chapter : chapters) { @@ -743,7 +760,7 @@ public final void readDataFull(Path folder, HolderLookup.Provider provider) { markDirty(); } - FTBQuests.LOGGER.info("Loaded {} chapter groups, {} chapters, {} quests, {} reward tables", chapterGroups.size(), chapterCounter, questCounter, rewardTables.size()); + FTBQuests.LOGGER.info("Loaded {} chapter groups, {} chapters, {} quests, {} reward tables", chapterGroups.size(), chapterCounter, questCounter, rewardTables.size()); } private void handleLegacyFileNBT(CompoundTag fileNBT) { @@ -943,7 +960,7 @@ public final void writeNetDataFull(RegistryFriendlyByteBuf buffer) { } } - FTBQuests.LOGGER.debug("Wrote {} bytes, {} objects", buffer.writerIndex() - pos, questObjectMap.size()); + FTBQuests.LOGGER.debug("Wrote {} bytes, {} objects", buffer.writerIndex() - pos, questObjectMap.size()); } public final void readNetDataFull(RegistryFriendlyByteBuf buffer) { @@ -1033,12 +1050,12 @@ public final void readNetDataFull(RegistryFriendlyByteBuf buffer) { } } - refreshIDMap(); - for (RewardTable table : rewardTables) { table.readNetData(buffer); } + refreshIDMap(); + for (ChapterGroup group : chapterGroups) { if (!group.isDefaultGroup()) { group.readNetData(buffer); @@ -1061,7 +1078,7 @@ public final void readNetDataFull(RegistryFriendlyByteBuf buffer) { } } - FTBQuests.LOGGER.info("Read {} bytes, {} objects", buffer.readerIndex() - pos, questObjectMap.size()); + FTBQuests.LOGGER.info("Read {} bytes, {} objects", buffer.readerIndex() - pos, questObjectMap.size()); } @Override @@ -1195,22 +1212,22 @@ public long readID(@Nullable Tag tag) { } public long getID(@Nullable Object obj) { - switch (obj) { - case null -> { - return 0L; - } - case Number n -> { - return n.longValue(); - } - case NumericTag nt -> { - return nt.getAsLong(); - } - case StringTag st -> { - return getID(st.getAsString()); - } - default -> { - } - } + switch (obj) { + case null -> { + return 0L; + } + case Number n -> { + return n.longValue(); + } + case NumericTag nt -> { + return nt.getAsLong(); + } + case StringTag st -> { + return getID(st.getAsString()); + } + default -> { + } + } String idStr = obj.toString(); long id = parseCodeString(idStr); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java index d38753b3..e61ae740 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java @@ -9,6 +9,7 @@ import dev.ftb.mods.ftblibrary.math.Bits; import dev.ftb.mods.ftblibrary.snbt.SNBTCompoundTag; import dev.ftb.mods.ftblibrary.ui.BaseScreen; +import dev.ftb.mods.ftblibrary.util.NetworkHelper; import dev.ftb.mods.ftblibrary.util.TooltipList; import dev.ftb.mods.ftblibrary.util.client.ClientUtils; import dev.ftb.mods.ftbquests.client.ClientQuestFile; @@ -17,15 +18,19 @@ import dev.ftb.mods.ftbquests.client.gui.RewardTablesScreen; import dev.ftb.mods.ftbquests.client.gui.quests.QuestScreen; import dev.ftb.mods.ftbquests.integration.RecipeModHelper; +import dev.ftb.mods.ftbquests.net.CreateObjectResponseMessage; import dev.ftb.mods.ftbquests.net.EditObjectMessage; +import dev.ftb.mods.ftbquests.net.SyncTranslationMessageToClient; import dev.ftb.mods.ftbquests.quest.*; import dev.ftb.mods.ftbquests.quest.reward.ItemReward; import dev.ftb.mods.ftbquests.quest.reward.Reward; import dev.ftb.mods.ftbquests.quest.reward.RewardType; import dev.ftb.mods.ftbquests.quest.reward.RewardTypes; +import dev.ftb.mods.ftbquests.quest.translation.TranslationKey; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; +import net.minecraft.Util; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -33,8 +38,11 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -63,7 +71,7 @@ public RewardTable(long id, BaseQuestFile file, String filename) { this.filename = filename; weightedRewards = new ArrayList<>(); - fakeQuest = new Quest(-1L, new Chapter(-1L, this.file, file.getDefaultChapterGroup())); + fakeQuest = makeFakeQuest(file); emptyWeight = 0f; lootSize = 1; hideTooltip = false; @@ -72,6 +80,21 @@ public RewardTable(long id, BaseQuestFile file, String filename) { lootTableId = null; } + private static Quest makeFakeQuest(BaseQuestFile file) { + return new Quest(-1L, new Chapter(-1L, file, file.getDefaultChapterGroup())); + } + + public static boolean isFakeQuestId(long id) { + return id == -1L; + } + + @NotNull + public static QuestObjectBase createRewardForTable(long id, String type, BaseQuestFile file) { + Reward reward = RewardType.createReward(id, makeFakeQuest(file), type); + Validate.isTrue(reward != null, "Unknown reward type!"); + return reward; + } + public Component getTitleOrElse(Component def) { return useTitle ? getTitle() : def; } @@ -157,28 +180,28 @@ public void writeData(CompoundTag nbt, HolderLookup.Provider provider) { ListTag list = new ListTag(); for (WeightedReward wr : weightedRewards) { - SNBTCompoundTag nbt1 = new SNBTCompoundTag(); - wr.getReward().writeData(nbt1, provider); + SNBTCompoundTag rewardTag = new SNBTCompoundTag(); + rewardTag.putString("id", wr.getReward().getCodeString()); + wr.getReward().writeData(rewardTag, provider); + wr.getReward().addAnyProtoTranslations(rewardTag); if (wr.getReward().getType() != RewardTypes.ITEM) { - nbt1.putString("type", wr.getReward().getType().getTypeForNBT()); + rewardTag.putString("type", wr.getReward().getType().getTypeForNBT()); } if (wr.getWeight() != 1f) { - nbt1.putFloat("weight", wr.getWeight()); + rewardTag.putFloat("weight", wr.getWeight()); } - if (nbt1.size() < 3) { - nbt1.singleLine(); + if (rewardTag.size() < 3) { + rewardTag.singleLine(); } - list.add(nbt1); + list.add(rewardTag); } nbt.put("rewards", list); if (lootCrate != null) { - CompoundTag nbt1 = new CompoundTag(); - lootCrate.writeData(nbt1); - nbt.put("loot_crate", nbt1); + nbt.put("loot_crate", Util.make(new CompoundTag(), tag -> lootCrate.writeData(tag))); } if (lootTableId != null) { @@ -194,24 +217,49 @@ public void readData(CompoundTag nbt, HolderLookup.Provider provider) { hideTooltip = nbt.getBoolean("hide_tooltip"); useTitle = nbt.getBoolean("use_title"); + Set prevRewards = weightedRewards.stream().map(wr -> wr.getReward().getId()).collect(Collectors.toSet()); weightedRewards.clear(); - ListTag list = nbt.getList("rewards", Tag.TAG_COMPOUND); + boolean refreshIds = false; + + ListTag list = nbt.getList("rewards", Tag.TAG_COMPOUND); for (int i = 0; i < list.size(); i++) { - CompoundTag nbt1 = list.getCompound(i); - Reward reward = RewardType.createReward(0L, fakeQuest, nbt1.getString("type")); + boolean newReward = false; + CompoundTag rewardTag = list.getCompound(i); + long rewardId = file.getID(rewardTag.get("id")); + if (rewardId == 0L) { + // should only occur on server when the client has sent a reward table with new reward(s) + rewardId = file.newID(); + rewardTag.putString("id", QuestObjectBase.getCodeString(rewardId)); + newReward = refreshIds = true; + } + Reward reward = RewardType.createReward(rewardId, fakeQuest, rewardTag.getString("type")); if (reward != null) { - reward.readData(nbt1, provider); - weightedRewards.add(new WeightedReward(reward, nbt1.contains("weight") ? nbt1.getFloat("weight") : 1)); + getQuestFile().getTranslationManager().processInitialTranslation(rewardTag, reward); + reward.readData(rewardTag, provider); + weightedRewards.add(new WeightedReward(reward, rewardTag.contains("weight") ? rewardTag.getFloat("weight") : 1)); + prevRewards.remove(rewardId); + if (newReward && getFile() instanceof ServerQuestFile sqf) { + NetworkHelper.sendToAll(sqf.server, CreateObjectResponseMessage.create(reward, rewardTag)); + } } } - lootCrate = null; + if (refreshIds) { + file.refreshRewardTableRewardIDs(); + } + + // clean up translations for any rewards that are no longer in the list + if (getFile().isServerSide()) { + prevRewards.forEach(id -> getFile().deleteObject(id)); + } if (nbt.contains("loot_crate")) { lootCrate = new LootCrate(this, false); lootCrate.readData(nbt.getCompound("loot_crate")); + } else { + lootCrate = null; } lootTableId = nbt.contains("loot_table_id") ? ResourceLocation.tryParse(nbt.getString("loot_table_id")) : null; @@ -232,6 +280,7 @@ public void writeNetData(RegistryFriendlyByteBuf buffer) { buffer.writeVarInt(weightedRewards.size()); for (WeightedReward wr : weightedRewards) { + buffer.writeLong(wr.getReward().getId()); buffer.writeVarInt(wr.getReward().getType().intId); wr.getReward().writeNetData(buffer); buffer.writeFloat(wr.getWeight()); @@ -261,8 +310,9 @@ public void readNetData(RegistryFriendlyByteBuf buffer) { int s = buffer.readVarInt(); for (int i = 0; i < s; i++) { + long id = buffer.readLong(); RewardType type = file.getRewardType(buffer.readVarInt()); - Reward reward = type.createReward(0L, fakeQuest); + Reward reward = type.createReward(id, fakeQuest); reward.readNetData(buffer); float weight = buffer.readFloat(); weightedRewards.add(new WeightedReward(reward, weight)); @@ -327,6 +377,12 @@ public void editedFromGUI() { @Override public void editedFromGUIOnServer() { file.updateLootCrates(); + + MinecraftServer server = ((ServerQuestFile) file).server; + + weightedRewards.forEach(wr -> + file.getTranslationManager().getStringTranslation(wr.getReward(), file.getLocale(), TranslationKey.TITLE).ifPresent(title -> + NetworkHelper.sendToAll(server, SyncTranslationMessageToClient.create(wr.getReward(), file.getLocale(), TranslationKey.TITLE, title)))); } @Override @@ -447,4 +503,14 @@ public LootCrate toggleLootCrate() { public boolean shouldShowTooltip() { return !hideTooltip; } + + public RewardTable copy() { + RewardTable copy = QuestObjectBase.copy(this, () -> new RewardTable(this.id, this.getFile())); + if (copy != null) { + copy.setRawTitle(getRawTitle()); + copy.weightedRewards.clear(); + getWeightedRewards().forEach(wr -> copy.weightedRewards.add(wr.copy())); + } + return copy; + } } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/WeightedReward.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/WeightedReward.java index 1f4cf860..499912db 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/WeightedReward.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/WeightedReward.java @@ -1,6 +1,8 @@ package dev.ftb.mods.ftbquests.quest.loot; +import dev.ftb.mods.ftbquests.quest.QuestObjectBase; import dev.ftb.mods.ftbquests.quest.reward.Reward; +import dev.ftb.mods.ftbquests.quest.reward.RewardType; public class WeightedReward implements Comparable { private final Reward reward; @@ -50,4 +52,9 @@ public static String chanceString(float weight, float totalWeight) { public int compareTo(WeightedReward o) { return Float.compare(weight, o.weight); } + + public WeightedReward copy() { + Reward r = QuestObjectBase.copy(reward, () -> RewardType.createReward(reward.id, reward.getQuest(), reward.getType().getTypeId().toString())); + return new WeightedReward(r, weight); + } } \ No newline at end of file diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/Reward.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/Reward.java index 1ea7d1ba..ddb94016 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/Reward.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/Reward.java @@ -13,6 +13,7 @@ import dev.ftb.mods.ftbquests.integration.RecipeModHelper; import dev.ftb.mods.ftbquests.net.ClaimRewardMessage; import dev.ftb.mods.ftbquests.quest.*; +import dev.ftb.mods.ftbquests.quest.translation.TranslationKey; import dev.ftb.mods.ftbquests.util.ProgressChange; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -303,4 +304,10 @@ public boolean ignoreRewardBlocking() { protected boolean isIgnoreRewardBlockingHardcoded() { return false; } + + public void addAnyProtoTranslations(CompoundTag tag) { + if (protoTranslations.containsKey(TranslationKey.TITLE)) { + getQuestFile().getTranslationManager().addInitialTranslation(tag, getQuestFile().getLocale(), TranslationKey.TITLE, protoTranslations.get(TranslationKey.TITLE)); + } + } } \ No newline at end of file diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/StageReward.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/StageReward.java index efddf88f..c7c2aa25 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/StageReward.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/reward/StageReward.java @@ -17,11 +17,16 @@ public class StageReward extends Reward { private String stage = ""; private boolean remove = false; - public StageReward(long id, Quest quest) { + public StageReward(long id, Quest quest, String stage) { super(id, quest); + this.stage = stage; autoclaim = RewardAutoClaim.INVISIBLE; } + public StageReward(long id, Quest quest) { + this(id, quest, ""); + } + @Override public RewardType getType() { return RewardTypes.STAGE; From b854cf26be45cab00a7370d4b2c855df1a776897 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 9 Jan 2025 14:29:14 +0000 Subject: [PATCH 17/24] fix: tooltips appearing behind context menus --- .../dev/ftb/mods/ftbquests/client/gui/quests/QuestScreen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestScreen.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestScreen.java index a6ff6e76..09c11388 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestScreen.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/QuestScreen.java @@ -115,7 +115,7 @@ public boolean doesGuiPauseGame() { @Override public int getMaxZLevel() { - return Z_LEVEL + 100; + return 2000; // to keep tooltips above context menus with a possibly high Z translation } @Override From 3f03c67b95d2dba1682a7d935ae1166b4c19d7c2 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 9 Jan 2025 14:38:44 +0000 Subject: [PATCH 18/24] chore: changelog updated --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1dfe2b8..5f020a0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2101.1.2] +### Added +* Added a new "None" quest shape, which simply means to not draw any border around the quest icon + * This doesn't affect the icon itself, which is still rendered as normal +* Locked quests (i.e. which can't be started due to dependencies) now show a small padlock icon + * This can be disabled in player preferences if preferred - "Show Icon for Locked Quests" +* Added a new "All Table" reward type + * This works on an existing reward table, and rewards the player with one of every reward in the table + ### Fixed * Fixed a rare server-side crash which can occur when new (as in, never on this server before) players join the server * Timing issue with FTB Teams initialising their team data for the first time @@ -13,6 +21,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Image objects with no click action can now be clicked-through, allowing the background to be scrolled/panned * Fixed issue with several ftbquests subcommands meaning they could not be used in MC functions * Commands are `/ftbquests change_progress`, `/ftbquests open_book`, and `/ftbquests export_reward_table_to_chest` +* Fixed tooltips sometimes rendering underneath context menus +* Fixed rewards in reward tables not being able to have a custom title set + * Technical detail: rewards in reward tables (unlike rewards in quests) have historically not had a unique ID, but this ID is necessary for titles to work with the new translation system which the mod uses. +* Fixed recipes for craftable items (task screens etc.) not working +* Fixed loot tables for task screens not working +* The "Open Wiki" entry in the Settings context menu now opens the new FTB Quests docs at https://docs.feed-the-beast.com/docs/mods/suite/Quests/ +* The "Download Quest Files" entry in the Settings menu now also saves the `lang/` folder + * Caveat: only translations which the client knows about are included in this download (translations on the server that the client has not used won't be included) ## [2101.1.1] From 920eaaec31363d01015816cc37c1ad570c9f188c Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Thu, 9 Jan 2025 15:55:03 +0000 Subject: [PATCH 19/24] fix: fix to last reward table commit --- .../ftb/mods/ftbquests/quest/loot/RewardTable.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java index e61ae740..403b0510 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/loot/RewardTable.java @@ -226,9 +226,15 @@ public void readData(CompoundTag nbt, HolderLookup.Provider provider) { for (int i = 0; i < list.size(); i++) { boolean newReward = false; CompoundTag rewardTag = list.getCompound(i); - long rewardId = file.getID(rewardTag.get("id")); - if (rewardId == 0L) { - // should only occur on server when the client has sent a reward table with new reward(s) + if (!rewardTag.contains("id") && file.isServerSide()) { + // can happen on server when reading in an older quest book where reward table rewards didn't have IDs + rewardTag.putString("id", QuestObjectBase.getCodeString(file.newID())); + } + long rewardId = QuestObjectBase.parseCodeString(rewardTag.getString("id")); + if (rewardId == 0L && file.isServerSide()) { + // Can happen on server when the client has sent a reward table with new reward(s) + // Note: can also happen on client when copying rewards that haven't been sent to server yet (reward editor screen) + // - in that case, an id of 0 is fine, so don't do anything here rewardId = file.newID(); rewardTag.putString("id", QuestObjectBase.getCodeString(rewardId)); newReward = refreshIds = true; From a8922a94bfb62d6cc628664b6a6aac1749fb9d93 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 10 Jan 2025 12:09:05 +0000 Subject: [PATCH 20/24] feat: added "Grab Copy of Item" to item task context menus Useful to extract items with custom components (e.g. FFS Smart Filters) if you don't have a copy of the item to hand. --- .../client/gui/quests/TaskButton.java | 28 ++++++++++++++- .../ftbquests/net/FTBQuestsNetHandler.java | 1 + .../net/GiveItemToPlayerMessage.java | 36 +++++++++++++++++++ .../assets/ftbquests/lang/en_us.json | 2 ++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/dev/ftb/mods/ftbquests/net/GiveItemToPlayerMessage.java diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/TaskButton.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/TaskButton.java index b2d6ed36..da63c93a 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/TaskButton.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/TaskButton.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; +import dev.architectury.networking.NetworkManager; import dev.ftb.mods.ftblibrary.icon.Color4I; import dev.ftb.mods.ftblibrary.icon.Icons; import dev.ftb.mods.ftblibrary.icon.ItemIcon; @@ -14,6 +15,7 @@ import dev.ftb.mods.ftbquests.client.gui.ContextMenuBuilder; import dev.ftb.mods.ftbquests.integration.item_filtering.ItemMatchingSystem; import dev.ftb.mods.ftbquests.net.EditObjectMessage; +import dev.ftb.mods.ftbquests.net.GiveItemToPlayerMessage; import dev.ftb.mods.ftbquests.quest.task.ItemTask; import dev.ftb.mods.ftbquests.quest.task.Task; import dev.ftb.mods.ftbquests.quest.theme.property.ThemeProperties; @@ -21,14 +23,17 @@ import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; public class TaskButton extends Button { private final QuestScreen questScreen; @@ -64,7 +69,7 @@ public void onClicked(MouseButton button) { ContextMenuBuilder builder = ContextMenuBuilder.create(task, questScreen); - if (task instanceof ItemTask itemTask) { + if (task instanceof ItemTask itemTask && !itemTask.getItemStack().isEmpty()) { var tags = itemTask.getItemStack().getItem().builtInRegistryHolder().tags().toList(); if (!tags.isEmpty() && !ItemMatchingSystem.INSTANCE.isItemFilter(itemTask.getItemStack())) { for (ItemFilterAdapter adapter : ItemMatchingSystem.INSTANCE.adapters()) { @@ -82,6 +87,9 @@ public void onClicked(MouseButton button) { } } } + builder.insertAtTop(List.of(new ContextMenuItem(Component.translatable("ftbquests.task.grab_item"), Icons.ADD_GRAY, + b -> NetworkManager.sendToServer(new GiveItemToPlayerMessage(itemTask.getItemStack())))) + ); } if (task.getIcon() instanceof ItemIcon itemIcon) { builder.insertAtTop(List.of(new ContextMenuItem(Component.translatable("ftbquests.gui.use_as_quest_icon"), @@ -99,6 +107,24 @@ public void onClicked(MouseButton button) { } } + private static String stackToString(ItemStack itemStack) { + StringBuilder builder = new StringBuilder(BuiltInRegistries.ITEM.getKey(itemStack.getItem()).toString()); + + String compData = itemStack.getComponents().stream() + .filter(c -> !c.value().equals(itemStack.getItem().components().get(c.type()))) + .map(c -> c.type() + "=\"" + c.value() + "\"") + .collect(Collectors.joining(",")); + if (!compData.isEmpty()) { + builder.append("[").append(compData).append("]"); + } + + if (itemStack.getCount() > 1) { + builder.append(" ").append(itemStack.getCount()); + } + + return builder.toString(); + } + private void setTagFilterAndSave(ItemTask itemTask, ItemFilterAdapter adapter, TagKey tag) { itemTask.setStackAndCount(adapter.makeTagFilterStack(tag), itemTask.getItemStack().getCount()); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/FTBQuestsNetHandler.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/FTBQuestsNetHandler.java index f6d8a228..05e0ab1b 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/net/FTBQuestsNetHandler.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/FTBQuestsNetHandler.java @@ -16,6 +16,7 @@ public static void init() { NetworkHelper.registerC2S(DeleteObjectMessage.TYPE, DeleteObjectMessage.STREAM_CODEC, DeleteObjectMessage::handle); NetworkHelper.registerC2S(EditObjectMessage.TYPE, EditObjectMessage.STREAM_CODEC, EditObjectMessage::handle); NetworkHelper.registerC2S(ForceSaveMessage.TYPE, ForceSaveMessage.STREAM_CODEC, ForceSaveMessage::handle); + NetworkHelper.registerC2S(GiveItemToPlayerMessage.TYPE, GiveItemToPlayerMessage.STREAM_CODEC, GiveItemToPlayerMessage::handle); NetworkHelper.registerC2S(GetEmergencyItemsMessage.TYPE, GetEmergencyItemsMessage.STREAM_CODEC, GetEmergencyItemsMessage::handle); NetworkHelper.registerC2S(MoveChapterGroupMessage.TYPE, MoveChapterGroupMessage.STREAM_CODEC, MoveChapterGroupMessage::handle); NetworkHelper.registerC2S(MoveChapterMessage.TYPE, MoveChapterMessage.STREAM_CODEC, MoveChapterMessage::handle); diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/net/GiveItemToPlayerMessage.java b/common/src/main/java/dev/ftb/mods/ftbquests/net/GiveItemToPlayerMessage.java new file mode 100644 index 00000000..ae4fc1eb --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftbquests/net/GiveItemToPlayerMessage.java @@ -0,0 +1,36 @@ +package dev.ftb.mods.ftbquests.net; + +import dev.architectury.hooks.item.ItemStackHooks; +import dev.architectury.networking.NetworkManager; +import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; +import dev.ftb.mods.ftbquests.integration.PermissionsHelper; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +public record GiveItemToPlayerMessage(ItemStack stack) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(FTBQuestsAPI.rl("give_item_to_player")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ItemStack.STREAM_CODEC, GiveItemToPlayerMessage::stack, + GiveItemToPlayerMessage::new + ); + + @Override + public Type type() { + return TYPE; + } + + public static void handle(GiveItemToPlayerMessage message, NetworkManager.PacketContext context) { + context.queue(() -> { + ServerPlayer player = (ServerPlayer) context.getPlayer(); + if (PermissionsHelper.hasEditorPermission(player, false)) { + ItemStackHooks.giveItem(player, message.stack); + player.displayClientMessage(Component.translatable("ftbquests.task.gave_item", message.stack.toString()), false); + } + }); + } +} diff --git a/common/src/main/resources/assets/ftbquests/lang/en_us.json b/common/src/main/resources/assets/ftbquests/lang/en_us.json index ad45a240..95bbb6c4 100644 --- a/common/src/main/resources/assets/ftbquests/lang/en_us.json +++ b/common/src/main/resources/assets/ftbquests/lang/en_us.json @@ -326,6 +326,8 @@ "ftbquests.task.auto_detected": "Items will be auto-detected", "ftbquests.task.click_to_submit_all": "Click to submit all items", "ftbquests.task.max_input": "Max Input Rate", + "ftbquests.task.grab_item": "Grab Copy of Item", + "ftbquests.task.gave_item": "Gave Task Item: %s", "ftbquests.task.ftbquests.item": "Item", "ftbquests.task.ftbquests.item.count": "Count", "ftbquests.task.ftbquests.item.consume_items": "Consume Items", From f61d7a23618eb5ddcf55f0d1168da5a89fedff56 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 10 Jan 2025 12:10:27 +0000 Subject: [PATCH 21/24] chore: changelog updated --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f020a0a..720fcdb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * This can be disabled in player preferences if preferred - "Show Icon for Locked Quests" * Added a new "All Table" reward type * This works on an existing reward table, and rewards the player with one of every reward in the table +* Added a new "Grab Copy of Item" context menu entry for tasks in the quest view panel + * This is intended to allow getting copies of items with custom components (e.g. FTB Filter System filters) if you don't have a copy of the item to hand ### Fixed * Fixed a rare server-side crash which can occur when new (as in, never on this server before) players join the server From 5adb844a4ee91987757c5d96511437e6b5169d3b Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 10 Jan 2025 12:19:01 +0000 Subject: [PATCH 22/24] chore: tiny changelog fix [ciskip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 720fcdb0..321066f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * This can be disabled in player preferences if preferred - "Show Icon for Locked Quests" * Added a new "All Table" reward type * This works on an existing reward table, and rewards the player with one of every reward in the table -* Added a new "Grab Copy of Item" context menu entry for tasks in the quest view panel +* Added a new "Grab Copy of Item" context menu entry for item tasks in the quest view panel * This is intended to allow getting copies of items with custom components (e.g. FTB Filter System filters) if you don't have a copy of the item to hand ### Fixed From 774552d5da5102c5c306430923c2fd64e734a0be Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 10 Jan 2025 14:40:33 +0000 Subject: [PATCH 23/24] fix: fixes from PR feedback --- .../client/gui/quests/OtherButtonsPanelBottom.java | 4 +++- .../java/dev/ftb/mods/ftbquests/quest/TeamData.java | 10 ++++++---- gradle.properties | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java index 0eeae802..bd6731d0 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/client/gui/quests/OtherButtonsPanelBottom.java @@ -29,6 +29,8 @@ import java.util.List; public class OtherButtonsPanelBottom extends OtherButtonsPanel { + private static final String WIKI_URL = "https://go.ftb.team/docs-quests"; + public OtherButtonsPanelBottom(Panel panel) { super(panel); } @@ -129,7 +131,7 @@ public void onClicked(MouseButton button) { contextMenu.add(new ContextMenuItem(Component.translatable("ftbquests.gui.reload_theme"), ThemeProperties.RELOAD_ICON.get(), b -> reload_theme())); contextMenu.add(new ContextMenuItem(Component.translatable("ftbquests.gui.wiki"), Icons.INFO, - b -> handleClick("https://docs.feed-the-beast.com/docs/mods/suite/Quests/"))); + b -> handleClick(WIKI_URL))); questScreen.openContextMenu(contextMenu); } diff --git a/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java b/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java index fc9b68ec..009b5eaa 100644 --- a/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java +++ b/common/src/main/java/dev/ftb/mods/ftbquests/quest/TeamData.java @@ -5,6 +5,7 @@ import dev.ftb.mods.ftblibrary.snbt.SNBT; import dev.ftb.mods.ftblibrary.snbt.SNBTCompoundTag; import dev.ftb.mods.ftbquests.FTBQuests; +import dev.ftb.mods.ftbquests.api.FTBQuestsAPI; import dev.ftb.mods.ftbquests.client.ClientQuestFile; import dev.ftb.mods.ftbquests.events.QuestProgressEventData; import dev.ftb.mods.ftbquests.net.*; @@ -32,6 +33,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import org.apache.commons.lang3.function.ToBooleanBiFunction; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; @@ -113,10 +115,10 @@ public BaseQuestFile getFile() { return file; } -// @NotNull -// public static TeamData get(Player player) { -// return FTBQuestsAPI.api().getQuestFile(player.getCommandSenderWorld().isClientSide()).getOrCreateTeamData(player); -// } + @NotNull + public static TeamData get(Player player) { + return FTBQuestsAPI.api().getQuestFile(player.getCommandSenderWorld().isClientSide()).getTeamData(player).orElseThrow(); + } public void markDirty() { shouldSave = true; diff --git a/gradle.properties b/gradle.properties index 65e33b59..f3e2dadf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ fabric_api_version=0.100.8+1.21 fabric_api_version_range=>=0.100.1+1.21 architectury_api_version=13.0.6 -ftb_library_version=2101.1.7-SNAPSHOT +ftb_library_version=2101.1.7 ftb_teams_version=2101.1.0 # Optional deps From bc22e96af8b273a60201459a0d66ad865050c768 Mon Sep 17 00:00:00 2001 From: Des Herriott Date: Fri, 10 Jan 2025 14:57:34 +0000 Subject: [PATCH 24/24] chore: changelog update [ciskip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 321066f7..fb65663e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Technical detail: rewards in reward tables (unlike rewards in quests) have historically not had a unique ID, but this ID is necessary for titles to work with the new translation system which the mod uses. * Fixed recipes for craftable items (task screens etc.) not working * Fixed loot tables for task screens not working -* The "Open Wiki" entry in the Settings context menu now opens the new FTB Quests docs at https://docs.feed-the-beast.com/docs/mods/suite/Quests/ +* The "Open Wiki" entry in the Settings context menu now opens the new FTB Quests docs at https://go.ftb.team/docs-quests * The "Download Quest Files" entry in the Settings menu now also saves the `lang/` folder * Caveat: only translations which the client knows about are included in this download (translations on the server that the client has not used won't be included)