From bd63e40ffa10e49354409f4f37d73b8fca667f08 Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:23:11 -0500 Subject: Waypoints refactor (#976) * Add ordered waypoints gui * Use InstancedUtils * Add fromColeweightJson * Add soopy waypoints importing * Refactor category to group * Add skyblocker format * Fix UI * Deprecate OrderedWaypoints and migrate to Waypoints * Start indices at 1 * Remove unused translations * Add Javadocs * Update waypoint text size * Refactor mining waypoints * Migrate from String to Location * Fix export translation key * Refactor individual waypoint * Remove unused translation strings * Update GoldorWaypointsManager * Migrate waypoints to both dwarven and crystal hollows * Refactor Waypoints api * Add Javadocs --- .../config/categories/MiningCategory.java | 16 - .../skyblocker/config/configs/MiningConfig.java | 2 + .../skyblock/chat/ChatPositionShare.java | 85 +++++ .../skyblock/chat/chatcoords/ChatLocation.java | 26 -- .../chat/chatcoords/ChatWaypointLocation.java | 66 ---- .../skyblock/dungeon/GoldorWaypointsManager.java | 16 +- .../skyblock/dungeon/secrets/SecretWaypoint.java | 16 +- .../skyblocker/skyblock/dwarven/CrystalsHud.java | 12 +- .../skyblock/dwarven/CrystalsLocationsManager.java | 6 +- .../skyblock/dwarven/MiningLocationLabel.java | 39 +- .../skyblocker/skyblock/rift/EnigmaSouls.java | 17 +- .../skyblock/waypoint/AbstractWaypointsScreen.java | 38 +- .../skyblocker/skyblock/waypoint/FairySouls.java | 22 +- .../skyblock/waypoint/IndividualWaypoint.java | 110 +++--- .../skyblock/waypoint/OrderedWaypoints.java | 391 +++------------------ .../hysky/skyblocker/skyblock/waypoint/Relics.java | 13 +- .../skyblocker/skyblock/waypoint/Waypoints.java | 155 +++++--- .../skyblock/waypoint/WaypointsListWidget.java | 128 ++++--- .../skyblock/waypoint/WaypointsScreen.java | 18 +- .../skyblock/waypoint/WaypointsShareScreen.java | 63 +++- .../de/hysky/skyblocker/utils/InstancedUtils.java | 5 +- .../java/de/hysky/skyblocker/utils/Location.java | 12 +- .../utils/waypoint/DistancedNamedWaypoint.java | 34 ++ .../skyblocker/utils/waypoint/NamedWaypoint.java | 78 ++-- .../utils/waypoint/OrderedNamedWaypoint.java | 93 +++++ .../utils/waypoint/ProfileAwareWaypoint.java | 2 +- .../hysky/skyblocker/utils/waypoint/Waypoint.java | 122 +++++-- .../utils/waypoint/WaypointCategory.java | 47 --- .../skyblocker/utils/waypoint/WaypointGroup.java | 169 +++++++++ .../resources/assets/skyblocker/lang/en_us.json | 33 +- .../hysky/skyblocker/utils/InstancedUtilsTest.java | 35 +- .../utils/waypoint/ProfileAwareWaypointTest.java | 8 +- .../utils/waypoint/WaypointCategoryTest.java | 39 -- .../utils/waypoint/WaypointGroupTest.java | 40 +++ .../skyblocker/utils/waypoint/WaypointsTest.java | 268 +++++++++++++- 35 files changed, 1304 insertions(+), 920 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chat/ChatPositionShare.java delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatLocation.java delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatWaypointLocation.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/waypoint/DistancedNamedWaypoint.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/waypoint/OrderedNamedWaypoint.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/waypoint/WaypointCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/waypoint/WaypointGroup.java delete mode 100644 src/test/java/de/hysky/skyblocker/utils/waypoint/WaypointCategoryTest.java create mode 100644 src/test/java/de/hysky/skyblocker/utils/waypoint/WaypointGroupTest.java (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java index 74be78cb..364e9b07 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java @@ -181,14 +181,6 @@ public class MiningCategory { newValue -> config.mining.crystalsWaypoints.enabled = newValue) .controller(ConfigUtils::createBooleanController) .build()) - .option(Option.createBuilder() - .name(Text.translatable("skyblocker.config.mining.crystalsWaypoints.textScale")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.crystalsWaypoints.textScale.@Tooltip"))) - .binding(defaults.mining.crystalsWaypoints.textScale, - () -> config.mining.crystalsWaypoints.textScale, - newValue -> config.mining.crystalsWaypoints.textScale = newValue) - .controller(FloatFieldControllerBuilder::create) - .build()) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.mining.crystalsWaypoints.findInChat")) .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.crystalsWaypoints.findInChat.@Tooltip"))) @@ -224,14 +216,6 @@ public class MiningCategory { newValue -> config.mining.commissionWaypoints.mode = newValue) .controller(ConfigUtils::createEnumCyclingListController) .build()) - .option(Option.createBuilder() - .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.textScale")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.textScale.@Tooltip"))) - .binding(defaults.mining.commissionWaypoints.textScale, - () -> config.mining.commissionWaypoints.textScale, - newValue -> config.mining.commissionWaypoints.textScale = newValue) - .controller(FloatFieldControllerBuilder::create) - .build()) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.useColor")) .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.useColor.@Tooltip"))) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java index 2ef5a4dc..d2b3beb5 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java @@ -102,6 +102,7 @@ public class MiningConfig { @SerialEntry public boolean enabled = true; + @Deprecated @SerialEntry public float textScale = 1; @@ -116,6 +117,7 @@ public class MiningConfig { @SerialEntry public CommissionWaypointMode mode = CommissionWaypointMode.BOTH; + @Deprecated @SerialEntry public float textScale = 1; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatPositionShare.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatPositionShare.java new file mode 100644 index 00000000..92f17a5f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatPositionShare.java @@ -0,0 +1,85 @@ +package de.hysky.skyblocker.skyblock.chat; + +import com.mojang.brigadier.Command; +import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatPositionShare { + private static final Logger LOGGER = LoggerFactory.getLogger(ChatPositionShare.class); + + private static final Pattern GENERIC_COORDS_PATTERN = Pattern.compile("x: (?-?[0-9]+), y: (?[0-9]+), z: (?-?[0-9]+)"); + private static final Pattern SKYBLOCKER_COORDS_PATTERN = Pattern.compile("x: (?-?[0-9]+), y: (?[0-9]+), z: (?-?[0-9]+)(?: \\| (?[^|]+))"); + private static final Pattern SKYHANNI_DIANA_PATTERN = Pattern.compile("A MINOS INQUISITOR has spawned near \\[(?[^]]*)] at Coords (?-?[0-9]+) (?[0-9]+) (?-?[0-9]+)"); + private static final List PATTERNS = List.of(SKYBLOCKER_COORDS_PATTERN, SKYHANNI_DIANA_PATTERN, GENERIC_COORDS_PATTERN); + + @Init + public static void init() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( + ClientCommandManager.literal("skyblocker").then(ClientCommandManager.literal("sharePosition").executes(context -> sharePlayerPosition(context.getSource()))) + )); + ClientReceiveMessageEvents.GAME.register(ChatPositionShare::onMessage); + } + + private static int sharePlayerPosition(FabricClientCommandSource source) { + Vec3d pos = source.getPosition(); + MessageScheduler.INSTANCE.sendMessageAfterCooldown("x: " + (int) pos.getX() + ", y: " + (int) pos.getY() + ", z: " + (int) pos.getZ() + " | " + Utils.getIslandArea(), true); + return Command.SINGLE_SUCCESS; + } + + private static void onMessage(Text text, boolean overlay) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.waypoints.enableWaypoints) { + String message = text.getString(); + + for (Pattern pattern : PATTERNS) { + Matcher matcher = pattern.matcher(message); + if (matcher.find()) { + try { + String x = matcher.group("x"); + String y = matcher.group("y"); + String z = matcher.group("z"); + String area = matcher.namedGroups().containsKey("area") ? matcher.group("area") : ""; + requestWaypoint(x, y, z, area); + } catch (Exception e) { + LOGGER.error("[Skyblocker Chat Waypoints] Error creating chat waypoint: ", e); + } + break; + } + } + } + } + + private static void requestWaypoint(String x, String y, String z, @NotNull String area) { + String command = "/skyblocker waypoints individual " + x + " " + y + " " + z + " " + area; + MutableText requestMessage = Constants.PREFIX.get().append(Text.translatable("skyblocker.config.chat.waypoints.display").formatted(Formatting.AQUA) + .styled(style -> style + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.translatable("skyblocker.config.chat.waypoints.display"))) + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)) + ) + ); + if (!area.isEmpty()) { + requestMessage = requestMessage.append(" at ").append(Text.literal(area).formatted(Formatting.AQUA)); + } + MinecraftClient.getInstance().player.sendMessage(requestMessage, false); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatLocation.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatLocation.java deleted file mode 100644 index d519a414..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatLocation.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.hysky.skyblocker.skyblock.chat.chatcoords; - -import com.mojang.brigadier.Command; -import de.hysky.skyblocker.annotations.Init; -import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.scheduler.MessageScheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; - -public class ChatLocation { - @Init - public static void init() { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( - ClientCommandManager.literal("skyblocker").then(ClientCommandManager.literal("location").executes(context -> sharePlayerLocation())) - )); - } - - private static int sharePlayerLocation() { - ClientPlayerEntity thePlayer = MinecraftClient.getInstance().player; - MessageScheduler.INSTANCE.sendMessageAfterCooldown("x: " + (int) thePlayer.getX() + ", y: " + (int) thePlayer.getY() + ", z: " + (int) thePlayer.getZ() + " | " + Utils.getIslandArea(), true); - return Command.SINGLE_SUCCESS; - } - -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatWaypointLocation.java b/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatWaypointLocation.java deleted file mode 100644 index b291a763..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/chat/chatcoords/ChatWaypointLocation.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.hysky.skyblocker.skyblock.chat.chatcoords; - -import de.hysky.skyblocker.annotations.Init; -import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.utils.Constants; -import de.hysky.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.ClickEvent; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ChatWaypointLocation { - - private static final Logger LOGGER = LoggerFactory.getLogger(ChatWaypointLocation.class); - - private static final Pattern GENERIC_COORDS_PATTERN = Pattern.compile("x: (?-?[0-9]+), y: (?[0-9]+), z: (?-?[0-9]+)"); - private static final Pattern SKYBLOCKER_COORDS_PATTERN = Pattern.compile("x: (?-?[0-9]+), y: (?[0-9]+), z: (?-?[0-9]+)(?: \\| (?[^|]+))"); - private static final Pattern SKYHANNI_DIANA_PATTERN = Pattern.compile("A MINOS INQUISITOR has spawned near \\[(?[^]]*)] at Coords (?-?[0-9]+) (?[0-9]+) (?-?[0-9]+)"); - private static final List PATTERNS = List.of(SKYBLOCKER_COORDS_PATTERN, SKYHANNI_DIANA_PATTERN, GENERIC_COORDS_PATTERN); - - @Init - public static void init() { - ClientReceiveMessageEvents.GAME.register(ChatWaypointLocation::onMessage); - } - - private static void onMessage(Text text, boolean overlay) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.waypoints.enableWaypoints) { - - String message = text.getString(); - - for (Pattern pattern : PATTERNS) { - Matcher matcher = pattern.matcher(message); - if (matcher.find()) { - try { - String x = matcher.group("x"); - String y = matcher.group("y"); - String z = matcher.group("z"); - String area = matcher.group("area"); - requestWaypoint(x, y, z, area); - } catch (Exception e) { - LOGGER.error("[SKYBLOCKER CHAT WAYPOINTS] Error creating chat waypoint: ", e); - } - break; - } - } - } - } - - private static void requestWaypoint(String x, String y, String z, String area) { - String command = "/skyblocker waypoints individual " + x + " " + y + " " + z + " " + area; - - Text text = Constants.PREFIX.get() - .append(Text.translatable("skyblocker.config.chat.waypoints.display").formatted(Formatting.AQUA) - .styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)))) - .append(Text.of(area != null ? " at " + area : "")); - - MinecraftClient.getInstance().player.sendMessage(text, false); - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java index 6d995ce8..2fd04655 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java @@ -11,6 +11,7 @@ import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.waypoint.NamedWaypoint; +import de.hysky.skyblocker.utils.waypoint.Waypoint; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; @@ -110,20 +111,15 @@ public class GoldorWaypointsManager { * @param waypoints The list of waypoints to operate on * @param playerName The name of the player to check against */ - private static void removeNearestWaypoint(ObjectArrayList waypoints, String playerName) { + private static void removeNearestWaypoint(List waypoints, String playerName) { MinecraftClient client = MinecraftClient.getInstance(); if (client.world == null) return; + // Get the position of the player with the given name Optional posOptional = client.world.getPlayers().stream().filter(player -> player.getGameProfile().getName().equals(playerName)).findAny().map(Entity::getPos); - if (posOptional.isPresent()) { - Vec3d pos = posOptional.get(); - - waypoints.stream().filter(GoldorWaypoint::shouldRender).min(Comparator.comparingDouble(waypoint -> waypoint.centerPos.squaredDistanceTo(pos))).map(waypoint -> { - waypoint.setShouldRender(false); - return null; - }); - } + // Find the nearest waypoint to the player and hide it + posOptional.flatMap(pos -> waypoints.stream().filter(GoldorWaypoint::shouldRender).min(Comparator.comparingDouble(waypoint -> waypoint.centerPos.squaredDistanceTo(pos)))).ifPresent(Waypoint::setFound); } /** @@ -143,7 +139,7 @@ public class GoldorWaypointsManager { * @param waypoints The set of waypoints to enable rendering for */ private static void enableAll(ObjectArrayList waypoints) { - waypoints.forEach(waypoint -> waypoint.setShouldRender(true)); + waypoints.forEach(Waypoint::setMissing); } /** diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java index 3779a66e..df3d9bf9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java @@ -7,19 +7,15 @@ import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.DungeonsConfig; -import de.hysky.skyblocker.utils.render.RenderHelper; -import de.hysky.skyblocker.utils.waypoint.NamedWaypoint; +import de.hysky.skyblocker.utils.waypoint.DistancedNamedWaypoint; import de.hysky.skyblocker.utils.waypoint.Waypoint; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; import net.minecraft.command.argument.EnumArgumentType; import net.minecraft.entity.Entity; import net.minecraft.text.Text; import net.minecraft.text.TextCodecs; -import net.minecraft.util.Formatting; import net.minecraft.util.StringIdentifiable; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +25,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.ToDoubleFunction; -public class SecretWaypoint extends NamedWaypoint { +public class SecretWaypoint extends DistancedNamedWaypoint { private static final Logger LOGGER = LoggerFactory.getLogger(SecretWaypoint.class); public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.INT.fieldOf("secretIndex").forGetter(secretWaypoint -> secretWaypoint.secretIndex), @@ -94,7 +90,7 @@ public class SecretWaypoint extends NamedWaypoint { @Override protected boolean shouldRenderName() { - return CONFIG.get().showSecretText; + return super.shouldRenderName() && CONFIG.get().showSecretText; } /** @@ -104,12 +100,6 @@ public class SecretWaypoint extends NamedWaypoint { public void render(WorldRenderContext context) { //TODO In the future, shrink the box for wither essence and items so its more realistic super.render(context); - - if (CONFIG.get().showSecretText) { - Vec3d posUp = centerPos.add(0, 1, 0); - double distance = context.camera().getPos().distanceTo(centerPos); - RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true); - } } @NotNull diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java index 80a3f232..8689df83 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java @@ -74,19 +74,17 @@ public class CrystalsHud { //if enabled add waypoint locations to map if (SkyblockerConfigManager.get().mining.crystalsHud.showLocations) { - Map ActiveWaypoints = CrystalsLocationsManager.activeWaypoints; - - for (MiningLocationLabel waypoint : ActiveWaypoints.values()) { - int waypointColor = waypoint.category().getColor(); - Vector2ic renderPos = transformLocation(waypoint.centerPos().getX(), waypoint.centerPos().getZ()); + for (MiningLocationLabel waypoint : CrystalsLocationsManager.activeWaypoints.values()) { + MiningLocationLabel.Category category = waypoint.category(); + Vector2ic renderPos = transformLocation(waypoint.centerPos.getX(), waypoint.centerPos.getZ()); int locationSize = SkyblockerConfigManager.get().mining.crystalsHud.locationSize; - if (SMALL_LOCATIONS.contains(waypoint.category().getName())) {//if small location half the location size + if (SMALL_LOCATIONS.contains(category.getName())) {//if small location half the location size locationSize /= 2; } //fill square of size locationSize around the coordinates of the location - context.fill(renderPos.x() - locationSize / 2, renderPos.y() - locationSize / 2, renderPos.x() + locationSize / 2, renderPos.y() + locationSize / 2, waypointColor); + context.fill(renderPos.x() - locationSize / 2, renderPos.y() - locationSize / 2, renderPos.x() + locationSize / 2, renderPos.y() + locationSize / 2, category.getColor()); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index 48dfdd34..a97e6051 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -294,8 +294,8 @@ public class CrystalsLocationsManager { public static int shareWaypoint(String place) { if (activeWaypoints.containsKey(place)) { - Vec3d pos = activeWaypoints.get(place).centerPos(); - MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + " " + place + ": " + (int) pos.getX() + ", " + (int) pos.getY() + ", " + (int) pos.getZ()); + BlockPos pos = activeWaypoints.get(place).pos; + MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + " " + place + ": " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ()); } else { //send fail message if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { @@ -349,7 +349,7 @@ public class CrystalsLocationsManager { String name = MiningLocationLabel.CrystalHollowsLocationsCategory.UNKNOWN.getName(); MiningLocationLabel unknownWaypoint = activeWaypoints.getOrDefault(name, null); if (unknownWaypoint != null) { - double distance = unknownWaypoint.centerPos().distanceTo(location.toCenterPos()); + double distance = unknownWaypoint.centerPos.distanceTo(location.toCenterPos()); if (distance < REMOVE_UNKNOWN_DISTANCE) { activeWaypoints.remove(name); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index 9aabd117..632ea3fc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -1,49 +1,42 @@ package de.hysky.skyblocker.skyblock.dwarven; +import com.mojang.serialization.Codec; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.render.RenderHelper; -import de.hysky.skyblocker.utils.render.Renderable; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; +import de.hysky.skyblocker.utils.waypoint.DistancedNamedWaypoint; import net.minecraft.text.Text; import net.minecraft.util.DyeColor; -import net.minecraft.util.Formatting; import net.minecraft.util.StringIdentifiable; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import java.awt.*; -import com.mojang.serialization.Codec; +public class MiningLocationLabel extends DistancedNamedWaypoint { + private final Category category; -// TODO: Clean up into the waypoint system with a new `DistancedWaypoint` that extends `NamedWaypoint` for this and secret waypoints. -public record MiningLocationLabel(Category category, Vec3d centerPos) implements Renderable { public MiningLocationLabel(Category category, BlockPos pos) { - this(category, pos.toCenterPos()); + // Set enabled to false in order to prevent the waypoint from being rendered, but the name text and distance will still be rendered. + super(pos, getName(category), new float[]{0, 0, 0}, false); + this.category = category; } - private Text getName() { + private static Text getName(Category category) { if (SkyblockerConfigManager.get().mining.commissionWaypoints.useColor) { return Text.literal(category.getName()).withColor(category.getColor()); } return Text.literal(category.getName()); } + public Category category() { + return category; + } + /** - * Renders the name and distance to the label scaled so can be seen at a distance - * - * @param context render context + * Override the {@link DistancedNamedWaypoint#shouldRenderName()} method to always return true, + * as the name should always be rendered, even though this waypoint is always disabled. */ @Override - public void render(WorldRenderContext context) { - Vec3d posUp = centerPos.add(0, 1, 0); - double distance = context.camera().getPos().distanceTo(centerPos); - //set scale config based on if in crystals or not - float textScale = Utils.isInCrystalHollows() ? SkyblockerConfigManager.get().mining.crystalsWaypoints.textScale : SkyblockerConfigManager.get().mining.commissionWaypoints.textScale; - float scale = (float) (textScale * (distance / 10)); - RenderHelper.renderText(context, getName(), posUp, scale, true); - RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, scale, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true); + protected boolean shouldRenderName() { + return true; } public interface Category { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/EnigmaSouls.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/EnigmaSouls.java index 8e3d1a91..e7d07f34 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/rift/EnigmaSouls.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/EnigmaSouls.java @@ -61,7 +61,7 @@ public class EnigmaSouls { for (int i = 0; i < waypoints.size(); i++) { JsonObject waypoint = waypoints.get(i).getAsJsonObject(); BlockPos pos = new BlockPos(waypoint.get("x").getAsInt(), waypoint.get("y").getAsInt(), waypoint.get("z").getAsInt()); - SOUL_WAYPOINTS.put(pos, new ProfileAwareWaypoint(pos, TYPE_SUPPLIER, GREEN, RED)); + SOUL_WAYPOINTS.put(pos, new EnigmaSoul(pos, TYPE_SUPPLIER, GREEN, RED)); } } catch (IOException e) { @@ -114,9 +114,7 @@ public class EnigmaSouls { if (Utils.isInTheRift() && config.enigmaSoulWaypoints && soulsLoaded.isDone()) { for (Waypoint soul : SOUL_WAYPOINTS.values()) { - if (soul.shouldRender()) { - soul.render(context); - } else if (config.highlightFoundEnigmaSouls) { + if (soul.shouldRender() || config.highlightFoundEnigmaSouls) { soul.render(context); } } @@ -161,4 +159,15 @@ public class EnigmaSouls { .filter(soul -> soul.pos.getSquaredDistance(player.getPos()) <= 16) .ifPresent(Waypoint::setFound); } + + private static class EnigmaSoul extends ProfileAwareWaypoint { + public EnigmaSoul(BlockPos pos, Supplier typeSupplier, float[] missingColor, float[] foundColor) { + super(pos, typeSupplier, missingColor, foundColor); + } + + @Override + public boolean shouldRender() { + return super.shouldRender() || SkyblockerConfigManager.get().otherLocations.rift.highlightFoundEnigmaSouls; + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/AbstractWaypointsScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/AbstractWaypointsScreen.java index da6f52c8..8111f41e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/AbstractWaypointsScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/AbstractWaypointsScreen.java @@ -5,7 +5,7 @@ import com.google.common.collect.MultimapBuilder; import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.waypoint.NamedWaypoint; -import de.hysky.skyblocker.utils.waypoint.WaypointCategory; +import de.hysky.skyblocker.utils.waypoint.WaypointGroup; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; @@ -13,20 +13,20 @@ import java.util.Arrays; public abstract class AbstractWaypointsScreen extends Screen { protected final T parent; - protected final Multimap waypoints; - protected String island; + protected final Multimap waypoints; + protected Location island; protected WaypointsListWidget waypointsListWidget; protected DropdownWidget islandWidget; public AbstractWaypointsScreen(Text title, T parent) { - this(title, parent, MultimapBuilder.hashKeys().arrayListValues().build()); + this(title, parent, MultimapBuilder.enumKeys(Location.class).arrayListValues().build()); } - public AbstractWaypointsScreen(Text title, T parent, Multimap waypoints) { - this(title, parent, waypoints, Utils.getLocationRaw()); + public AbstractWaypointsScreen(Text title, T parent, Multimap waypoints) { + this(title, parent, waypoints, Utils.getLocation()); } - public AbstractWaypointsScreen(Text title, T parent, Multimap waypoints, String island) { + public AbstractWaypointsScreen(Text title, T parent, Multimap waypoints, Location island) { super(title); this.parent = parent; this.waypoints = waypoints; @@ -36,8 +36,8 @@ public abstract class AbstractWaypointsScreen extends Screen { @Override protected void init() { super.init(); - waypointsListWidget = addDrawableChild(new WaypointsListWidget(client, this, width, height - 96, 32, 24)); - islandWidget = addDrawableChild(new DropdownWidget<>(client, width - 160, 8, 150, height - 8, Arrays.asList(Location.values()), this::islandChanged, Location.from(island))); + waypointsListWidget = addDrawableChild(new WaypointsListWidget(client, this, width, height - 120, 32, 24)); + islandWidget = addDrawableChild(new DropdownWidget<>(client, width - 160, 8, 150, height - 8, Arrays.asList(Location.values()), this::islandChanged, island)); } @Override @@ -59,13 +59,27 @@ public abstract class AbstractWaypointsScreen extends Screen { } protected void islandChanged(Location location) { - island = location.id(); + island = location; waypointsListWidget.setIsland(island); } - protected abstract boolean isEnabled(NamedWaypoint waypoint); + /** + * Gets whether the waypoint is enabled in the current screen. + * Override for custom behavior such as using the checkbox for whether it should be included in the exported waypoints. + * + * @return whether the waypoint is enabled in the current screen + */ + protected boolean isEnabled(NamedWaypoint waypoint) { + return waypoint.isEnabled(); + } - protected abstract void enabledChanged(NamedWaypoint waypoint, boolean enabled); + /** + * Called when the enabled state of a waypoint checkbox changes. + * Override for custom behavior such as updating whether the waypoint should be included in the exported waypoints. + */ + protected void enabledChanged(NamedWaypoint waypoint, boolean enabled) { + waypoint.setEnabled(enabled); + } protected void updateButtons() { waypointsListWidget.updateButtons(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/FairySouls.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/FairySouls.java index c5b48b93..c3a32106 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/FairySouls.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/FairySouls.java @@ -9,11 +9,7 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.HelperConfig; -import de.hysky.skyblocker.utils.ColorUtils; -import de.hysky.skyblocker.utils.Constants; -import de.hysky.skyblocker.utils.NEURepoManager; -import de.hysky.skyblocker.utils.PosUtils; -import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.*; import de.hysky.skyblocker.utils.waypoint.ProfileAwareWaypoint; import de.hysky.skyblocker.utils.waypoint.Waypoint; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; @@ -76,7 +72,7 @@ public class FairySouls { private static void loadFairySouls() { fairySoulsLoaded = NEURepoManager.runAsyncAfterLoad(() -> { maxSouls = NEURepoManager.NEU_REPO.getConstants().getFairySouls().getMaxSouls(); - NEURepoManager.NEU_REPO.getConstants().getFairySouls().getSoulLocations().forEach((location, fairiesForLocation) -> fairySouls.put(location, fairiesForLocation.stream().map(coordinate -> new BlockPos(coordinate.getX(), coordinate.getY(), coordinate.getZ())).collect(Collectors.toUnmodifiableMap(pos -> pos, pos -> new ProfileAwareWaypoint(pos, TYPE_SUPPLIER, ColorUtils.getFloatComponents(DyeColor.GREEN), ColorUtils.getFloatComponents(DyeColor.RED)))))); + NEURepoManager.NEU_REPO.getConstants().getFairySouls().getSoulLocations().forEach((location, fairiesForLocation) -> fairySouls.put(location, fairiesForLocation.stream().map(coordinate -> new BlockPos(coordinate.getX(), coordinate.getY(), coordinate.getZ())).collect(Collectors.toUnmodifiableMap(pos -> pos, pos -> new FairySoul(pos, TYPE_SUPPLIER, ColorUtils.getFloatComponents(DyeColor.GREEN), ColorUtils.getFloatComponents(DyeColor.RED)))))); LOGGER.debug("[Skyblocker] Loaded {} fairy souls across {} locations", fairySouls.values().stream().mapToInt(Map::size).sum(), fairySouls.size()); try (BufferedReader reader = Files.newBufferedReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json"))) { @@ -200,4 +196,18 @@ public class FairySouls { fairiesForLocation.values().forEach(ProfileAwareWaypoint::setMissing); } } + + private static class FairySoul extends ProfileAwareWaypoint { + public FairySoul(BlockPos pos, Supplier typeSupplier, float[] missingColor, float[] foundColor) { + super(pos, typeSupplier, missingColor, foundColor); + } + + /** + * Less strict than the check {@link FairySouls#render(WorldRenderContext)} since this only needs to ensure found fairy souls are rendered if the config is enabled. + */ + @Override + public boolean shouldRender() { + return super.shouldRender() || SkyblockerConfigManager.get().helpers.fairySouls.highlightFoundSouls; + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/IndividualWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/IndividualWaypoint.java index 7a470373..02fc24c2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/IndividualWaypoint.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/IndividualWaypoint.java @@ -6,6 +6,7 @@ import com.mojang.brigadier.arguments.StringArgumentType; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.utils.ColorUtils; +import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.waypoint.NamedWaypoint; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; @@ -16,69 +17,70 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import java.awt.*; +import java.util.function.Consumer; +/** + * One single temporary waypoint that gets deleted when the player gets close or changes world. + * Used for sharing positions from chat or other temporary uses. + */ public class IndividualWaypoint extends NamedWaypoint { + private static IndividualWaypoint waypoint; - private static IndividualWaypoint waypoint; + @Init + public static void init() { + ClientTickEvents.END_CLIENT_TICK.register(IndividualWaypoint::onTick); + WorldRenderEvents.AFTER_TRANSLUCENT.register(context -> {if (waypoint != null) waypoint.render(context);}); + ClientPlayConnectionEvents.JOIN.register((ignore, ignore2, ignore3) -> waypoint = null); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( + ClientCommandManager.literal(SkyblockerMod.NAMESPACE).then(ClientCommandManager.literal("waypoints").then(ClientCommandManager.literal("individual") + .then(ClientCommandManager.argument("x", IntegerArgumentType.integer(Integer.MIN_VALUE)) + .then(ClientCommandManager.argument("y", IntegerArgumentType.integer(Integer.MIN_VALUE)) + .then(ClientCommandManager.argument("z", IntegerArgumentType.integer(Integer.MIN_VALUE)) + .then(ClientCommandManager.argument("area", StringArgumentType.greedyString()) + .executes(context -> setWaypoint( + context.getSource()::sendFeedback, + IntegerArgumentType.getInteger(context, "x"), + IntegerArgumentType.getInteger(context, "y"), + IntegerArgumentType.getInteger(context, "z"), + StringArgumentType.getString(context, "area") + )) + ) + ) + ) + ) + ))) + ); + } - @Init - public static void init() { + public IndividualWaypoint(BlockPos pos, Text name, float[] colorComponents) { + super(pos, name, colorComponents, DEFAULT_HIGHLIGHT_ALPHA, true); + } - ClientTickEvents.END_CLIENT_TICK.register(IndividualWaypoint::onTick); + private static int setWaypoint(Consumer feedback, int x, int y, int z, String area) { + setWaypoint(x, y, z, area); + feedback.accept(Constants.PREFIX.get().append(Text.translatable("skyblocker.config.chat.waypoints.displayed", x, y, z, area))); + return Command.SINGLE_SUCCESS; + } - WorldRenderEvents.AFTER_TRANSLUCENT.register(context -> { if (waypoint != null) waypoint.render(context); }); + private static void setWaypoint(int x, int y, int z, String area) { + String waypointName = area != null && !area.isEmpty() ? area : "Chat Waypoint"; - ClientPlayConnectionEvents.JOIN.register((ignore, ignore2, ignore3) -> waypoint = null); + Text waypointDisplay; + if (waypointName.charAt(0) == '⏣') { + waypointDisplay = Text.literal("⏣").formatted(Formatting.DARK_PURPLE) + .append(Text.literal(waypointName.substring(1)).formatted(Formatting.AQUA)); + } else { + waypointDisplay = Text.literal(waypointName).formatted(Formatting.AQUA); + } - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( - ClientCommandManager.literal(SkyblockerMod.NAMESPACE) - .then(ClientCommandManager.literal("waypoints") - .then(ClientCommandManager.literal("individual") - .then(ClientCommandManager.argument("x", IntegerArgumentType.integer(Integer.MIN_VALUE)) - .then(ClientCommandManager.argument("y", IntegerArgumentType.integer(Integer.MIN_VALUE)) - .then(ClientCommandManager.argument("z", IntegerArgumentType.integer(Integer.MIN_VALUE)) - .then(ClientCommandManager.argument("area", StringArgumentType.greedyString()) - .executes(context -> setWaypoint( - IntegerArgumentType.getInteger(context, "x"), - IntegerArgumentType.getInteger(context, "y"), - IntegerArgumentType.getInteger(context, "z"), - StringArgumentType.getString(context, "area") - )) - ) - ) - ) - ) - ) - ) - )); - } - - public IndividualWaypoint(BlockPos pos, Text name, float[] colorComponents) { - super(pos, name, colorComponents, 0.5f, true); - } - - private static int setWaypoint(int x, int y, int z, String area) { - String waypointName = area != null && !area.isEmpty() ? area : "Waypoint"; - - Text waypointDisplay; - if (waypointName.charAt(0) == '⏣') { - waypointDisplay = Text.literal("⏣").formatted(Formatting.DARK_PURPLE) - .append(Text.literal(waypointName.substring(1)).formatted(Formatting.AQUA)); - } else { - waypointDisplay = Text.literal(waypointName).formatted(Formatting.AQUA); - } - - waypoint = new IndividualWaypoint(new BlockPos(x, y, z), waypointDisplay, ColorUtils.getFloatComponents(Color.GREEN.getRGB())); - return Command.SINGLE_SUCCESS; - } - - private static void onTick(MinecraftClient c) { - if (waypoint != null && c.player.getPos().distanceTo(Vec3d.ofCenter(waypoint.pos)) <= 8) { - waypoint = null; - } - } + waypoint = new IndividualWaypoint(new BlockPos(x, y, z), waypointDisplay, ColorUtils.getFloatComponents(Color.GREEN.getRGB())); + } + private static void onTick(MinecraftClient client) { + if (waypoint != null && client.player != null && client.player.squaredDistanceTo(waypoint.centerPos) <= 8) { + waypoint = null; + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java index 34c3a16b..5c82cf7b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java @@ -1,11 +1,9 @@ package de.hysky.skyblocker.skyblock.waypoint; import com.google.common.primitives.Floats; -import com.google.gson.Gson; import com.google.gson.JsonParser; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; @@ -13,299 +11,82 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.utils.CodecUtils; -import de.hysky.skyblocker.utils.ColorUtils; import de.hysky.skyblocker.utils.Constants; -import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType; -import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument; -import de.hysky.skyblocker.utils.command.argumenttypes.color.ColorArgumentType; -import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.waypoint.OrderedNamedWaypoint; import de.hysky.skyblocker.utils.waypoint.Waypoint; +import de.hysky.skyblocker.utils.waypoint.WaypointGroup; import it.unimi.dsi.fastutil.floats.FloatArrayList; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.command.CommandSource; import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import org.slf4j.Logger; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Semaphore; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import static com.mojang.brigadier.arguments.StringArgumentType.getString; -import static com.mojang.brigadier.arguments.StringArgumentType.word; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; +/** + * @deprecated Use {@link Waypoints} instead. + */ +@Deprecated public class OrderedWaypoints { private static final Logger LOGGER = LogUtils.getLogger(); private static final Codec> SERIALIZATION_CODEC = Codec.unboundedMap(Codec.STRING, OrderedWaypointGroup.CODEC).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new); private static final String PREFIX = "[Skyblocker::OrderedWaypoints::v1]"; - private static final Path PATH = SkyblockerMod.CONFIG_DIR.resolve("ordered_waypoints.json"); - private static final Map WAYPOINTS = new Object2ObjectOpenHashMap<>(); - private static final Semaphore SEMAPHORE = new Semaphore(1); - private static final Object2IntOpenHashMap INDEX_STORE = new Object2IntOpenHashMap<>(); - private static final int RADIUS = 2; - private static final float[] LIGHT_GRAY = { 192 / 255f, 192 / 255f, 192 / 255f }; - - private static CompletableFuture loaded; - private static boolean showAll; + public static final Path PATH = SkyblockerMod.CONFIG_DIR.resolve("ordered_waypoints.json"); @Init public static void init() { ClientLifecycleEvents.CLIENT_STARTED.register(_client -> load()); - ClientLifecycleEvents.CLIENT_STOPPING.register(_client -> save()); ClientCommandRegistrationCallback.EVENT.register(OrderedWaypoints::registerCommands); - WorldRenderEvents.AFTER_TRANSLUCENT.register(OrderedWaypoints::render); } private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { dispatcher.register(literal(SkyblockerMod.NAMESPACE) .then(literal("waypoints") .then(literal("ordered") - .then(literal("add") - .then(argument("groupName", word()) - .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) - .then(argument("pos", ClientBlockPosArgumentType.blockPos()) - .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE, Integer.MIN_VALUE)) - .then(argument("hex", ColorArgumentType.hex()) - .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE, ColorArgumentType.getIntFromHex(context, "hex"))))))) - .then(literal("addAt") - .then(argument("groupName", word()) - .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) - .then(argument("index", IntegerArgumentType.integer(0)) - .then(argument("pos", ClientBlockPosArgumentType.blockPos()) - .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), IntegerArgumentType.getInteger(context, "index"), Integer.MIN_VALUE)) - .then(argument("hex", ColorArgumentType.hex()) - .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), IntegerArgumentType.getInteger(context, "index"), ColorArgumentType.getIntFromHex(context, "hex")))))))) - .then(literal("remove") - .then(argument("groupName", word()) - .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) - .executes(context -> removeWaypointGroup(context.getSource(), getString(context, "groupName"))) - .then(argument("pos", ClientBlockPosArgumentType.blockPos()) - .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE))))) - .then(literal("removeAt") - .then(argument("groupName", word()) - .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) - .then(argument("index", IntegerArgumentType.integer(0)) - .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), null, IntegerArgumentType.getInteger(context, "index")))))) - .then(literal("toggle") - .then(argument("groupName", word()) - .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) - .executes(context -> toggleGroup(context.getSource(), getString(context, "groupName"))))) - .then(literal("showAll") - .executes(context -> showAll(context.getSource()))) .then(literal("import") - .then(literal("coleWeight") - .then(argument("groupName", word()) - .executes(context -> fromColeWeightFormat(context.getSource(), getString(context, "groupName"))))) .then(literal("skyblocker") - .executes(context -> fromSkyblockerFormat(context.getSource())))) - .then(literal("export") - .executes(context -> export(context.getSource())))))); - } - - private static int addWaypoint(FabricClientCommandSource source, String groupName, ClientPosArgument posArgument, int index, int color) { - BlockPos pos = posArgument.toAbsoluteBlockPos(source); - - SEMAPHORE.acquireUninterruptibly(); - - float[] colorComponents = color != Integer.MIN_VALUE ? ColorUtils.getFloatComponents(color) : new float[0]; - - OrderedWaypointGroup group = WAYPOINTS.computeIfAbsent(groupName, name -> new OrderedWaypointGroup(name, true, new ObjectArrayList<>())); - OrderedWaypoint waypoint = new OrderedWaypoint(pos, colorComponents); - - if (index != Integer.MIN_VALUE) { - int indexToAddAt = Math.clamp(index, 0, group.waypoints().size()); - - group.waypoints().add(indexToAddAt, waypoint); - INDEX_STORE.removeInt(group.name()); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.addAt.success", group.name(), indexToAddAt))); - } else { - group.waypoints().add(waypoint); - INDEX_STORE.removeInt(group.name()); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.add.success", group.name(), pos.toShortString()))); - } - - SEMAPHORE.release(); - - return Command.SINGLE_SUCCESS; - } - - private static int removeWaypointGroup(FabricClientCommandSource source, String groupName) { - if (WAYPOINTS.containsKey(groupName)) { - SEMAPHORE.acquireUninterruptibly(); - WAYPOINTS.remove(groupName); - INDEX_STORE.removeInt(groupName); - SEMAPHORE.release(); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.removeGroup.success", groupName))); - } else { - source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); - } - - return Command.SINGLE_SUCCESS; - } - - private static int removeWaypoint(FabricClientCommandSource source, String groupName, ClientPosArgument posArgument, int index) { - if (WAYPOINTS.containsKey(groupName)) { - SEMAPHORE.acquireUninterruptibly(); - OrderedWaypointGroup group = WAYPOINTS.get(groupName); - - if (posArgument != null) { - BlockPos pos = posArgument.toAbsoluteBlockPos(source); - - group.waypoints().removeIf(waypoint -> waypoint.getPos().equals(pos)); - INDEX_STORE.removeInt(group.name()); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.remove.success", pos.toShortString(), group.name()))); - } - - if (index != Integer.MIN_VALUE) { - int indexToRemove = Math.clamp(index, 0, group.waypoints().size() - 1); - - group.waypoints().remove(indexToRemove); - INDEX_STORE.removeInt(group.name()); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.removeAt.success", indexToRemove, group.name()))); - } - - SEMAPHORE.release(); - } else { - source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); - } - - return Command.SINGLE_SUCCESS; - } - - private static int toggleGroup(FabricClientCommandSource source, String groupName) { - if (WAYPOINTS.containsKey(groupName)) { - SEMAPHORE.acquireUninterruptibly(); - WAYPOINTS.put(groupName, WAYPOINTS.get(groupName).toggle()); - SEMAPHORE.release(); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.toggle.success", groupName))); - } else { - source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); - } - - return Command.SINGLE_SUCCESS; - } - - private static int showAll(FabricClientCommandSource source) { - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.showAll"))); - showAll = !showAll; - - return Command.SINGLE_SUCCESS; - } - - private static void render(WorldRenderContext wrc) { - if ((Utils.isInCrystalHollows() || Utils.isInDwarvenMines()) && loaded.isDone() && SEMAPHORE.tryAcquire()) { - for (OrderedWaypointGroup group : WAYPOINTS.values()) { - if (group.enabled()) { - List waypoints = group.waypoints(); - if (waypoints.isEmpty()) continue; - - if (!showAll) { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - int centreIndex = INDEX_STORE.computeIfAbsent(group.name(), name -> 0); - - for (int i = 0; i < waypoints.size(); i++) { - OrderedWaypoint waypoint = waypoints.get(i); - - if (waypoint.getPos().isWithinDistance(player.getPos(), RADIUS)) { - centreIndex = i; - INDEX_STORE.put(group.name(), i); - - break; - } - } - - int previousIndex = (centreIndex - 1 + waypoints.size()) % waypoints.size(); - int currentIndex = (centreIndex + waypoints.size()) % waypoints.size(); - int nextIndex = (centreIndex + 1) % waypoints.size(); - - OrderedWaypoint previous = waypoints.get(previousIndex); - OrderedWaypoint current = waypoints.get(currentIndex); - OrderedWaypoint next = waypoints.get(nextIndex); - - previous.render(wrc, RelativeIndex.PREVIOUS, previousIndex); - current.render(wrc, RelativeIndex.CURRENT, currentIndex); - next.render(wrc, RelativeIndex.NEXT, nextIndex); - - RenderHelper.renderLineFromCursor(wrc, Vec3d.ofCenter(next.getPos().up()), LIGHT_GRAY, 1f, 5f); - } else { - for (int i = 0; i < waypoints.size(); i++) { - //Render them as white by default - waypoints.get(i).render(wrc, RelativeIndex.CURRENT, i); - } - } - } - } - - SEMAPHORE.release(); - } + .executes(context -> fromSkyblockerFormat(context.getSource()))))))); } + /** + * Loads and migrates the ordered waypoints to waypoints. + * @deprecated Use {@link Waypoints} instead. + */ + @Deprecated private static void load() { - loaded = CompletableFuture.runAsync(() -> { - try (BufferedReader reader = Files.newBufferedReader(PATH)) { - WAYPOINTS.putAll(SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow()); - } catch (NoSuchFileException ignored) { - } catch (Exception e) { - LOGGER.error("[Skyblocker Ordered Waypoints] Failed to load the waypoints! :(", e); - } - }); - } - - private static void save() { - try (BufferedWriter writer = Files.newBufferedWriter(PATH)) { - SkyblockerMod.GSON.toJson(SERIALIZATION_CODEC.encodeStart(JsonOps.INSTANCE, WAYPOINTS).getOrThrow(), writer); + try (BufferedReader reader = Files.newBufferedReader(PATH)) { + Map orderedWaypoints = SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow(); + migrateOrderedWaypoints(orderedWaypoints); + Files.move(PATH, SkyblockerMod.CONFIG_DIR.resolve("legacy_ordered_waypoints.json")); + LOGGER.info("[Skyblocker Ordered Waypoints] Successfully migrated {} ordered waypoints from {} groups to waypoints!", orderedWaypoints.values().stream().map(OrderedWaypointGroup::waypoints).mapToInt(List::size).sum(), orderedWaypoints.size()); + } catch (NoSuchFileException ignored) { } catch (Exception e) { - LOGGER.error("[Skyblocker Ordered Waypoints] Failed to save the waypoints! :(", e); + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to load the waypoints! :(", e); } } - private static int export(FabricClientCommandSource source) { - try { - String json = new Gson().toJson(SERIALIZATION_CODEC.encodeStart(JsonOps.INSTANCE, WAYPOINTS).getOrThrow()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPOutputStream gzip = new GZIPOutputStream(out); - - gzip.write(json.getBytes()); - gzip.close(); - - String encoded = new String(Base64.getEncoder().encode(out.toByteArray())); - String exportCode = PREFIX + encoded; - - MinecraftClient.getInstance().keyboard.setClipboard(exportCode); - source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.export.success"))); - } catch (Exception e) { - LOGGER.error("[Skyblocker Ordered Waypoints] Failed to export waypoints!", e); - source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.export.fail"))); - } - - return Command.SINGLE_SUCCESS; - } - - //TODO in future handle for when the group names clash? + /** + * @deprecated Use {@link Waypoints} instead. + */ + @Deprecated private static int fromSkyblockerFormat(FabricClientCommandSource source) { try { String importCode = MinecraftClient.getInstance().keyboard.getClipboard(); @@ -317,10 +98,8 @@ public class OrderedWaypoints { String json = new String(new GZIPInputStream(new ByteArrayInputStream(decoded)).readAllBytes()); Map importedWaypoints = SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(json)).getOrThrow(); - SEMAPHORE.acquireUninterruptibly(); - WAYPOINTS.putAll(importedWaypoints); + migrateOrderedWaypoints(importedWaypoints); source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.skyblocker.success"))); - SEMAPHORE.release(); } else { source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.skyblocker.un