diff --git a/build.gradle b/build.gradle index dad92e19..825dff56 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,7 @@ dependencies { include modImplementation("dev.xpple:clientarguments:${project.clientarguments_version}") include modImplementation("dev.xpple:betterconfig-fabric:${project.betterconfig_version}") + include modImplementation("dev.xpple:simplewaypoints:${project.simplewaypoints_version}") includedLibrary "com.seedfinding:mc_feature:${project.seedfinding_feature_version}" includedLibrary "com.seedfinding:mc_biome:${project.seedfinding_biome_version}" @@ -181,6 +182,7 @@ modrinth { dependencies { required.project 'fabric-api' embedded.project 'betterconfig' + embedded.project 'simplewaypoints' } } tasks.modrinth.onlyIf { diff --git a/gradle.properties b/gradle.properties index 60b38803..cb6a0861 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,12 +7,12 @@ org.gradle.jvmargs=-Xmx2G minecraft_version_dependency=>=1.21.6 <=1.21.8 minecraft_version_list=1.21.6,1.21.7,1.21.8 minecraft_version_list_presentable=1.21.6 - 1.21.8 - loader_version=0.16.14 - loom_version=1.10-SNAPSHOT + loader_version=0.17.2 + loom_version=1.11-SNAPSHOT # check these on https://maven.parchmentmc.org/org/parchmentmc/data/ - parchment_mcversion=1.21.6 - parchment_version=2025.06.29 + parchment_mcversion=1.21.8 + parchment_version=2025.07.20 # Mod Properties mod_version=2.11.3 @@ -21,9 +21,10 @@ org.gradle.jvmargs=-Xmx2G # Dependencies # also check this on https://fabricmc.net/develop/ - fabric_version=0.129.0+1.21.8 - clientarguments_version=1.11.3 + fabric_version=0.131.0+1.21.8 + clientarguments_version=1.11.4 betterconfig_version=2.4.0 + simplewaypoints_version=1.0.0-alpha.4 seedfinding_core_version=1.210.0 seedfinding_biome_version=1.171.1 seedfinding_feature_version=1.171.10 @@ -31,7 +32,7 @@ org.gradle.jvmargs=-Xmx2G latticg_version=1.07 mapping_io_version=0.7.1 devauth_version=1.2.1 - checkstyle_version=10.26.1 + checkstyle_version=11.0.0 jazzer_junit_version=0.24.0 - junit_version=5.13.2 + junit_version=5.13.4 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9bbc975c..8bdaf60c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1..2a84e188 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index faf93008..ef07e016 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a218..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 47a1ed1c..5fd8593e 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils; import dev.xpple.betterconfig.api.BetterConfigAPI; import dev.xpple.betterconfig.api.ModConfigBuilder; +import dev.xpple.simplewaypoints.api.SimpleWaypointsAPI; import net.earthcomputer.clientcommands.command.*; import net.earthcomputer.clientcommands.event.ClientConnectionEvents; import net.earthcomputer.clientcommands.features.CommandExecutionCustomPayload; @@ -15,6 +16,7 @@ import net.earthcomputer.clientcommands.features.PlayerRandCracker; import net.earthcomputer.clientcommands.features.Relogger; import net.earthcomputer.clientcommands.features.ServerBrandManager; +import net.earthcomputer.clientcommands.features.Waypoints; import net.earthcomputer.clientcommands.render.RenderQueue; import net.earthcomputer.clientcommands.util.MappingsHelper; import net.fabricmc.api.ClientModInitializer; @@ -70,6 +72,9 @@ public void onInitializeClient() { } }); + Waypoints.migrateWaypoints(); + SimpleWaypointsAPI.getInstance().registerCommandAlias("cwaypoint"); + MappingsHelper.load(); // Registration @@ -82,7 +87,6 @@ public void onInitializeClient() { FishingCracker.registerEvents(); PlayerRandCracker.registerEvents(); ServerBrandManager.registerEvents(); - WaypointCommand.registerEvents(); } private static void setupScrambleWindowTitle() { @@ -186,7 +190,6 @@ public static void registerCommands(CommandDispatcher UsageTreeCommand.register(dispatcher); UuidCommand.register(dispatcher); VarCommand.register(dispatcher); - WaypointCommand.register(dispatcher); WeatherCommand.register(dispatcher); WhisperEncryptedCommand.register(dispatcher); WikiCommand.register(dispatcher); diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java deleted file mode 100644 index 50415b2d..00000000 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ /dev/null @@ -1,428 +0,0 @@ -package net.earthcomputer.clientcommands.command; - -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.logging.LogUtils; -import com.mojang.serialization.Dynamic; -import net.earthcomputer.clientcommands.ClientCommands; -import net.earthcomputer.clientcommands.event.MoreWorldRenderEvents; -import net.earthcomputer.clientcommands.render.RenderQueue; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; -import net.minecraft.ChatFormatting; -import net.minecraft.SharedConstants; -import net.minecraft.Util; -import net.minecraft.client.Camera; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.multiplayer.ClientChunkCache; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.ShapeRenderer; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.NbtOps; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.ComponentUtils; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.VisibleForTesting; -import org.joml.Vector2d; -import org.slf4j.Logger; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static com.mojang.brigadier.arguments.BoolArgumentType.*; -import static com.mojang.brigadier.arguments.StringArgumentType.*; -import static dev.xpple.clientarguments.arguments.CBlockPosArgument.*; -import static dev.xpple.clientarguments.arguments.CDimensionArgument.*; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; - -public class WaypointCommand { - private static final ResourceLocation HUD_LAYER_ID = ResourceLocation.fromNamespaceAndPath("clientcommands", "waypoints"); - - private static final Map> waypoints = new HashMap<>(); - - private static final Logger LOGGER = LogUtils.getLogger(); - - private static final SimpleCommandExceptionType SAVE_FAILED_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cwaypoint.saveFailed")); - private static final DynamicCommandExceptionType ALREADY_EXISTS_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatable("commands.cwaypoint.alreadyExists", name)); - private static final DynamicCommandExceptionType NOT_FOUND_EXCEPTION = new DynamicCommandExceptionType(name -> Component.translatable("commands.cwaypoint.notFound", name)); - - static { - try { - loadFile(); - } catch (Exception e) { - LOGGER.error("Could not load waypoints file, hence /cwaypoint will not work!", e); - } - } - - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(literal("cwaypoint") - .then(literal("add") - .then(argument("name", word()) - .then(argument("pos", blockPos()) - .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) - .then(argument("dimension", dimension()) - .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"), getDimension(ctx, "dimension"))))))) - .then(literal("remove") - .then(argument("name", word()) - .suggests((ctx, builder) -> { - Map worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource().getClient())); - return SharedSuggestionProvider.suggest(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); - }) - .executes(ctx -> remove(ctx.getSource(), getString(ctx, "name"))))) - .then(literal("edit") - .then(argument("name", word()) - .suggests((ctx, builder) -> { - Map worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource().getClient())); - return SharedSuggestionProvider.suggest(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); - }) - .then(argument("pos", blockPos()) - .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) - .then(argument("dimension", dimension()) - .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"), getDimension(ctx, "dimension"))))))) - .then(literal("list") - .executes(ctx -> list(ctx.getSource())) - .then(argument("current", bool()) - .executes(ctx -> list(ctx.getSource(), getBool(ctx, "current")))))); - } - - private static String getWorldIdentifier(Minecraft minecraft) { - String worldIdentifier; - if (minecraft.hasSingleplayerServer()) { - // the level id remains the same even after the level is renamed - worldIdentifier = minecraft.getSingleplayerServer().storageSource.getLevelId(); - } else { - worldIdentifier = minecraft.getConnection().getConnection().getRemoteAddress().toString(); - } - return worldIdentifier; - } - - private static int add(FabricClientCommandSource source, String name, BlockPos pos) throws CommandSyntaxException { - return add(source, name, pos, source.getWorld().dimension()); - } - - private static int add(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source.getClient()); - - Map worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); - - if (worldWaypoints.putIfAbsent(name, new WaypointLocation(dimension, pos)) != null) { - throw ALREADY_EXISTS_EXCEPTION.create(name); - } - - saveFile(); - source.sendFeedback(Component.translatable("commands.cwaypoint.add.success", name, formatCoordinates(pos), dimension.location())); - return Command.SINGLE_SUCCESS; - } - - private static int remove(FabricClientCommandSource source, String name) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source.getClient()); - - Map worldWaypoints = waypoints.get(worldIdentifier); - - if (worldWaypoints == null) { - throw NOT_FOUND_EXCEPTION.create(name); - } - - if (worldWaypoints.remove(name) == null) { - throw NOT_FOUND_EXCEPTION.create(name); - } - - saveFile(); - source.sendFeedback(Component.translatable("commands.cwaypoint.remove.success", name)); - return Command.SINGLE_SUCCESS; - } - - private static int edit(FabricClientCommandSource source, String name, BlockPos pos) throws CommandSyntaxException { - return edit(source, name, pos, source.getWorld().dimension()); - } - - private static int edit(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source.getClient()); - - Map worldWaypoints = waypoints.get(worldIdentifier); - - if (worldWaypoints == null) { - throw NOT_FOUND_EXCEPTION.create(name); - } - - if (worldWaypoints.computeIfPresent(name, (key, value) -> new WaypointLocation(dimension, pos)) == null) { - throw NOT_FOUND_EXCEPTION.create(name); - } - - saveFile(); - source.sendFeedback(Component.translatable("commands.cwaypoint.edit.success", name, formatCoordinates(pos), dimension.location())); - return Command.SINGLE_SUCCESS; - } - - private static int list(FabricClientCommandSource source) { - return list(source, false); - } - - private static int list(FabricClientCommandSource source, boolean current) { - if (current) { - String worldIdentifier = getWorldIdentifier(source.getClient()); - - Map worldWaypoints = waypoints.get(worldIdentifier); - - if (worldWaypoints == null || worldWaypoints.isEmpty()) { - source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); - return 0; - } - - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, formatCoordinates(waypoint.location()), waypoint.dimension().location()))); - return worldWaypoints.size(); - } - - if (waypoints.isEmpty()) { - source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); - return 0; - } - - int[] count = {0}; - waypoints.forEach((worldIdentifier, worldWaypoints) -> { - if (worldWaypoints.isEmpty()) { - return; - } - - count[0] += worldWaypoints.size(); - - source.sendFeedback(Component.literal(worldIdentifier).append(":")); - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, formatCoordinates(waypoint.location()), waypoint.dimension().location()))); - }); - return count[0]; - } - - private static void saveFile() throws CommandSyntaxException { - try { - CompoundTag rootTag = new CompoundTag(); - rootTag.putInt("DataVersion", SharedConstants.getCurrentVersion().dataVersion().version()); - CompoundTag compoundTag = new CompoundTag(); - waypoints.forEach((worldIdentifier, worldWaypoints) -> compoundTag.put(worldIdentifier, worldWaypoints.entrySet().stream() - .collect(CompoundTag::new, (result, entry) -> { - CompoundTag waypoint = new CompoundTag(); - waypoint.store("pos", BlockPos.CODEC, entry.getValue().location()); - String dimension = entry.getValue().dimension().location().toString(); - waypoint.putString("Dimension", dimension); - result.put(entry.getKey(), waypoint); - }, CompoundTag::merge))); - rootTag.put("Waypoints", compoundTag); - Path newFile = Files.createTempFile(ClientCommands.CONFIG_DIR, "waypoints", ".dat"); - NbtIo.write(rootTag, newFile); - Path backupFile = ClientCommands.CONFIG_DIR.resolve("waypoints.dat_old"); - Path currentFile = ClientCommands.CONFIG_DIR.resolve("waypoints.dat"); - Util.safeReplaceFile(currentFile, newFile, backupFile); - } catch (IOException e) { - throw SAVE_FAILED_EXCEPTION.create(); - } - } - - private static void loadFile() throws Exception { - waypoints.clear(); - CompoundTag rootTag = NbtIo.read(ClientCommands.CONFIG_DIR.resolve("waypoints.dat")); - if (rootTag == null) { - return; - } - waypoints.putAll(deserializeWaypoints(rootTag)); - } - - @VisibleForTesting - public static Map> deserializeWaypoints(CompoundTag rootTag) { - Map> waypoints = new HashMap<>(); - - CompoundTag compoundTag = rootTag.getCompoundOrEmpty("Waypoints"); - compoundTag.keySet().forEach(worldIdentifier -> { - CompoundTag worldWaypoints = compoundTag.getCompoundOrEmpty(worldIdentifier); - waypoints.put(worldIdentifier, worldWaypoints.keySet().stream() - .collect(Collectors.toMap(Function.identity(), name -> { - CompoundTag waypoint = worldWaypoints.getCompoundOrEmpty(name); - BlockPos pos = waypoint.read("pos", BlockPos.CODEC).orElseThrow(); - ResourceKey dimension = Level.RESOURCE_KEY_CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); - return new WaypointLocation(dimension, pos); - }))); - }); - - return waypoints; - } - - private static Component formatCoordinates(BlockPos waypoint) { - return ComponentUtils.wrapInSquareBrackets(Component.literal(waypoint.toShortString())).withStyle(style -> style - .withColor(ChatFormatting.GREEN) - .withClickEvent(new ClickEvent.CopyToClipboard(waypoint.getX() + " " + waypoint.getY() + " " + waypoint.getZ())) - .withHoverEvent(new HoverEvent.ShowText(Component.translatable("chat.copy.click"))) - ); - } - - public static void registerEvents() { - HudElementRegistry.addLast(HUD_LAYER_ID, WaypointCommand::renderWaypointLabels); - MoreWorldRenderEvents.END_MAIN_PASS.register(WaypointCommand::renderWaypointBoxes); - } - - private static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { - String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); - Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); - if (waypoints == null) { - return; - } - - Minecraft minecraft = Minecraft.getInstance(); - GameRenderer gameRenderer = minecraft.gameRenderer; - Camera camera = gameRenderer.getMainCamera(); - Entity cameraEntity = camera.getEntity(); - float partialTicks = deltaTracker.getGameTimeDeltaPartialTick(true); - double verticalFovRad = Math.toRadians(gameRenderer.getFov(camera, partialTicks, false)); - Window window = minecraft.getWindow(); - double aspectRatio = (double) window.getGuiScaledWidth() / window.getGuiScaledHeight(); - double horizontalFovRad = 2 * Math.atan(Math.tan(verticalFovRad / 2) * aspectRatio); - - Vec3 viewVector3 = cameraEntity.getViewVector(1.0f); - Vector2d viewVector = new Vector2d(viewVector3.x, viewVector3.z); - Vector2d position = new Vector2d(cameraEntity.getEyePosition().x, cameraEntity.getEyePosition().z); - - List xPositions = new ArrayList<>(); - waypoints.forEach((waypointName, waypoint) -> { - if (!waypoint.dimension().location().equals(minecraft.level.dimension().location())) { - return; - } - - double distanceSquared = waypoint.location().distToCenterSqr(cameraEntity.position()); - long distance = Math.round(Math.sqrt(distanceSquared)); - Component label = ComponentUtils.wrapInSquareBrackets(Component.literal(waypointName + ' ' + distance).withStyle(ChatFormatting.YELLOW)); - - Vector2d waypointLocation = new Vector2d(waypoint.location().getX(), waypoint.location().getZ()); - double angleRad = viewVector.angle(waypointLocation.sub(position, new Vector2d())); - boolean right = angleRad > 0; - angleRad = Math.abs(angleRad); - - int x; - if (angleRad > horizontalFovRad / 2) { - int width = minecraft.font.width(label); - x = right ? guiGraphics.guiWidth() - width / 2 : width / 2; - } else { - // V is the view vector - // A is the leftmost visible direction - // B is the rightmost visible direction - // M is the intersection of the position -> waypoint line with AB - double mv = Math.tan(angleRad) * GameRenderer.PROJECTION_Z_NEAR; - double av = Math.tan(horizontalFovRad / 2) * GameRenderer.PROJECTION_Z_NEAR; - double ab = 2 * av; - double am = right ? mv + av : ab - (mv + av); - double perc = am / ab; - x = (int) (perc * guiGraphics.guiWidth()); - } - xPositions.add(new WaypointLabelLocation(label, x)); - }); - - xPositions.sort(Comparator.comparingInt(WaypointLabelLocation::location)); - - List> positions = new ArrayList<>(); - positions.add(xPositions); - - for (int line = 0; line < positions.size(); line++) { - List waypointLabelLocations = positions.get(line); - int i = 0; - while (i < waypointLabelLocations.size() - 1) { - WaypointLabelLocation left = waypointLabelLocations.get(i); - WaypointLabelLocation right = waypointLabelLocations.get(i + 1); - int leftX = left.location(); - int rightX = right.location(); - int leftWidth = minecraft.font.width(left.label()); - int rightWidth = minecraft.font.width(right.label()); - if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { - if (line + 1 == positions.size()) { - positions.add(new ArrayList<>()); - } - List nextLevel = positions.get(line + 1); - WaypointLabelLocation removed = waypointLabelLocations.remove(i + 1); - nextLevel.add(removed); - } else { - i++; - } - } - } - - for (int line = 0; line < positions.size(); line++) { - List w = positions.get(line); - for (WaypointLabelLocation waypoint : w) { - guiGraphics.drawCenteredString(minecraft.font, waypoint.label(), waypoint.location(), 1 + line * minecraft.font.lineHeight, 0xFFFFFFFF); - } - } - } - - private static void renderWaypointBoxes(WorldRenderContext context) { - String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); - Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); - if (waypoints == null) { - return; - } - - ClientChunkCache chunkSource = context.world().getChunkSource(); - waypoints.forEach((waypointName, waypoint) -> { - if (!waypoint.dimension().location().equals(context.world().dimension().location())) { - return; - } - - BlockPos waypointLocation = waypoint.location(); - if (!chunkSource.hasChunk(waypointLocation.getX() >> 4, waypointLocation.getZ() >> 4)) { - return; - } - - Vec3 cameraPosition = context.camera().getPosition(); - float distance = (float) waypointLocation.distToCenterSqr(cameraPosition); - distance = (float) Math.sqrt(distance) / 6; - - PoseStack stack = context.matrixStack(); - stack.pushPose(); - stack.translate(cameraPosition.scale(-1)); - - AABB box = new AABB(waypointLocation); - ShapeRenderer.renderLineBox(stack, context.consumers().getBuffer(RenderQueue.LINES_NO_DEPTH_LAYER), box, 1, 1, 1, 1); - - stack.translate(waypointLocation.getCenter().add(new Vec3(0, 1, 0))); - stack.mulPose(context.camera().rotation()); - stack.scale(0.025f * distance, -0.025f * distance, 0.025f * distance); - - Font font = Minecraft.getInstance().font; - int width = font.width(waypointName) / 2; - int backgroundColour = (int) (Minecraft.getInstance().options.getBackgroundOpacity(0.25f) * 255.0f) << 24; - font.drawInBatch(waypointName, -width, 0, 0xFFFFFFFF, false, stack.last().pose(), context.consumers(), Font.DisplayMode.SEE_THROUGH, backgroundColour, LightTexture.FULL_SKY); - - stack.popPose(); - }); - } - - @VisibleForTesting - public record WaypointLocation(ResourceKey dimension, BlockPos location) { - } - - record WaypointLabelLocation(Component label, int location) { - } -} diff --git a/src/main/java/net/earthcomputer/clientcommands/features/Waypoints.java b/src/main/java/net/earthcomputer/clientcommands/features/Waypoints.java new file mode 100644 index 00000000..199f5089 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/features/Waypoints.java @@ -0,0 +1,70 @@ +package net.earthcomputer.clientcommands.features; + +import com.mojang.logging.LogUtils; +import dev.xpple.simplewaypoints.SimpleWaypoints; +import net.earthcomputer.clientcommands.ClientCommands; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import org.slf4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +public final class Waypoints { + private Waypoints() { + } + + private static final Logger LOGGER = LogUtils.getLogger(); + + public static void migrateWaypoints() { + Path clientcommandsWaypointsPath = ClientCommands.CONFIG_DIR.resolve("waypoints.dat"); + if (!Files.isRegularFile(clientcommandsWaypointsPath)) { + return; // nothing to migrate, or migration already done + } + + LOGGER.info("clientcommands' waypoints.dat file detected, will be migrated to SimpleWaypoints"); + Path clientcommandsOldWaypointsPath = ClientCommands.CONFIG_DIR.resolve("waypoints.dat_old"); + boolean deleteOld; + try { + Files.copy(clientcommandsWaypointsPath, clientcommandsOldWaypointsPath, StandardCopyOption.REPLACE_EXISTING); + deleteOld = true; + } catch (IOException e) { + LOGGER.error("Could not back up waypoints.dat file", e); + deleteOld = false; + } + + Path simplewaypointsWaypointsPath = SimpleWaypoints.MOD_CONFIG_PATH.resolve("waypoints.dat"); + if (Files.isRegularFile(simplewaypointsWaypointsPath)) { + LOGGER.info("SimpleWaypoints' waypoints.dat file already exists, clientcommands' waypoints.dat file will be merged"); + try { + CompoundTag clientcommandsCompoundTag = NbtIo.read(clientcommandsWaypointsPath); + CompoundTag simplewaypointsCompoundTag = NbtIo.read(simplewaypointsWaypointsPath); + if (clientcommandsCompoundTag == null || simplewaypointsCompoundTag == null) { + throw new IOException("Could not parse clientcommands' or SimpleWaypoints' waypoint.dat file"); + } + simplewaypointsCompoundTag.merge(clientcommandsCompoundTag); + NbtIo.write(simplewaypointsCompoundTag, simplewaypointsWaypointsPath); + } catch (IOException e) { + LOGGER.error("Could not merge waypoints.dat file", e); + } + } else { + LOGGER.info("SimpleWaypoints' waypoints.dat does not exist yet, clientcommands' waypoints.dat file will be copied"); + try { + Files.copy(clientcommandsWaypointsPath, simplewaypointsWaypointsPath); + } catch (IOException e) { + LOGGER.error("Could not migrate waypoints.dat file", e); + } + } + + if (deleteOld) { + LOGGER.info("clientcommands's waypoint.dat will be deleted, waypoints.dat_old is kept"); + try { + Files.delete(clientcommandsWaypointsPath); + } catch (IOException e) { + LOGGER.error("Could not delete waypoints.dat file during migration", e); + } + } + } +} diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index c3b29cdb..bf836e5f 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -301,15 +301,6 @@ "commands.cvar.remove.success": "Successfully removed variable \"%s\"", "commands.cvar.saveFile.failed": "Could not save variables file", - "commands.cwaypoint.add.success": "Waypoint \"%s\" at %s in %s successfully added", - "commands.cwaypoint.alreadyExists": "A waypoint with the name \"%s\" already exists", - "commands.cwaypoint.edit.success": "Waypoint \"%s\" has successfully been changed to %s in %s", - "commands.cwaypoint.list": "- \"%s\" at %s in %s", - "commands.cwaypoint.list.empty": "No available waypoints", - "commands.cwaypoint.notFound": "No waypoint with the name \"%s\" could be found", - "commands.cwaypoint.remove.success": "Waypoint \"%s\" successfully removed", - "commands.cwaypoint.saveFailed": "Could not save waypoints file", - "commands.cwe.playerNotFound": "Player not found", "commands.cweather.reset": "Stopped overriding weather", diff --git a/src/main/resources/assets/clientcommands/lang/zh_cn.json b/src/main/resources/assets/clientcommands/lang/zh_cn.json index 12005cdf..d73e14f0 100644 --- a/src/main/resources/assets/clientcommands/lang/zh_cn.json +++ b/src/main/resources/assets/clientcommands/lang/zh_cn.json @@ -301,15 +301,6 @@ "commands.cvar.remove.success": "成功删除变量“%s”", "commands.cvar.saveFile.failed": "无法保存变量文件", - "commands.cwaypoint.add.success": "成功添加导航点“%s”位于%s在%s", - "commands.cwaypoint.alreadyExists": "名称为“%s”的导航点已经存在", - "commands.cwaypoint.edit.success": "导航点“%s”已成功修改到%s在%s", - "commands.cwaypoint.list": "- “%s”位于%s在%s", - "commands.cwaypoint.list.empty": "没有可用的导航点", - "commands.cwaypoint.notFound": "找不到名称为“%s”的导航点", - "commands.cwaypoint.remove.success": "导航点“%s”已被移除", - "commands.cwaypoint.saveFailed": "无法保存导航点文件", - "commands.cwe.playerNotFound": "未找到玩家", "commands.cweather.reset": "停止天气覆盖", diff --git a/src/main/resources/clientcommands.aw b/src/main/resources/clientcommands.aw index 6f90b18f..04168320 100644 --- a/src/main/resources/clientcommands.aw +++ b/src/main/resources/clientcommands.aw @@ -44,10 +44,6 @@ accessible method net/minecraft/client/Screenshot getFile (Ljava/io/File;)Ljava/ # cpermissionlevel accessible method net/minecraft/client/player/LocalPlayer getPermissionLevel ()I -# cwaypoint -accessible field net/minecraft/server/MinecraftServer storageSource Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess; -accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F - # Game Options accessible field net/minecraft/client/OptionInstance value Ljava/lang/Object; diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index d1b232e3..b78bff48 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -30,7 +30,8 @@ "fabricloader": ">=0.4.0", "fabric-api": "*", "betterconfig": "*", - "clientarguments": "*" + "clientarguments": "*", + "simplewaypoints": "*" }, "mixins": [ "mixins.clientcommands.json" diff --git a/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java b/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java deleted file mode 100644 index f6522609..00000000 --- a/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.earthcomputer.clientcommands.test; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.earthcomputer.clientcommands.command.WaypointCommand; -import net.minecraft.SharedConstants; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.server.Bootstrap; -import net.minecraft.world.level.Level; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public final class WaypointLoadingTest { - @BeforeAll - public static void setup() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - private static CompoundTag parseSnbt(String snbt) { - try { - return TagParser.parseCompoundAsArgument(new StringReader(snbt)); - } catch (CommandSyntaxException e) { - throw new AssertionError(e); - } - } - - @Test - public void testWaypointLoading() { - CompoundTag waypointTag = parseSnbt(""" - { - DataVersion: 4189, - Waypoints: { - foo: { - testWaypoint: { - pos: [I; 1, 2, 3], - Dimension: "minecraft:overworld" - } - } - } - } - """); - - var waypoints = WaypointCommand.deserializeWaypoints(waypointTag); - assertEquals(1, waypoints.size()); - assertTrue(waypoints.containsKey("foo")); - var worldWaypoints = waypoints.get("foo"); - assertEquals(1, worldWaypoints.size()); - assertTrue(worldWaypoints.containsKey("testWaypoint")); - var waypoint = worldWaypoints.get("testWaypoint"); - assertEquals(new BlockPos(1, 2, 3), waypoint.location()); - assertEquals(Level.OVERWORLD, waypoint.dimension()); - } -}