From f1db688a1bfe8d6b0ba2397bcab6fce94ec6658b Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Mon, 27 Mar 2023 22:32:45 +0200 Subject: [PATCH 01/29] Add the cwaypoint command --- .../clientcommands/ClientCommands.java | 1 + .../command/WaypointCommand.java | 166 ++++++++++++++++++ .../assets/clientcommands/lang/en_us.json | 8 + 3 files changed, 175 insertions(+) create mode 100644 src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 1aff60a85..edc442be7 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -84,5 +84,6 @@ public static void registerCommands(CommandDispatcher PosCommand.register(dispatcher); CrackRNGCommand.register(dispatcher); WeatherCommand.register(dispatcher); + WaypointCommand.register(dispatcher); } } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java new file mode 100644 index 000000000..76df7194f --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -0,0 +1,166 @@ +package net.earthcomputer.clientcommands.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandSource; +import net.minecraft.registry.RegistryKey; +import net.minecraft.text.Text; +import net.minecraft.util.Pair; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.mojang.brigadier.arguments.BoolArgumentType.*; +import static com.mojang.brigadier.arguments.StringArgumentType.*; +import static dev.xpple.clientarguments.arguments.CBlockPosArgumentType.*; +import static dev.xpple.clientarguments.arguments.CDimensionArgumentType.*; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; + +public class WaypointCommand { + + public static final Map>>> waypoints = new HashMap<>(); + + private static final DynamicCommandExceptionType ALREADY_EXISTS_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.alreadyExists", name)); + private static final DynamicCommandExceptionType NOT_FOUND_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.notFound", name)); + + 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"), getCBlockPos(ctx, "pos"))) + .then(argument("dimension", dimension()) + .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"), getCDimensionArgument(ctx, "dimension").getRegistryKey())))))) + .then(literal("remove") + .then(argument("name", word()) + .suggests((ctx, builder) -> { + Map>> worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource())); + return CommandSource.suggestMatching(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())); + return CommandSource.suggestMatching(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); + }) + .then(argument("pos", blockPos()) + .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"))) + .then(argument("dimension", dimension()) + .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"), getCDimensionArgument(ctx, "dimension").getRegistryKey())))))) + .then(literal("list") + .executes(ctx -> list(ctx.getSource())) + .then(argument("current", bool()) + .executes(ctx -> list(ctx.getSource(), getBool(ctx, "current")))))); + } + + private static String getWorldIdentifier(FabricClientCommandSource source) { + String worldIdentifier; + if (source.getClient().isIntegratedServerRunning()) { + worldIdentifier = source.getClient().getServer().getSaveProperties().getLevelName(); + } else { + worldIdentifier = source.getClient().getNetworkHandler().getConnection().getAddress().toString(); + } + return worldIdentifier; + } + + private static int add(FabricClientCommandSource source, String name, BlockPos pos) throws CommandSyntaxException { + return add(source, name, pos, source.getWorld().getRegistryKey()); + } + + private static int add(FabricClientCommandSource source, String name, BlockPos pos, RegistryKey dimension) throws CommandSyntaxException { + String worldIdentifier = getWorldIdentifier(source); + + Map>> worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); + + if (worldWaypoints.containsKey(name)) { + throw ALREADY_EXISTS_EXCEPTION.create(name); + } + + if (worldWaypoints.putIfAbsent(name, new Pair<>(pos, dimension)) != null) { + throw ALREADY_EXISTS_EXCEPTION.create(name); + } + + source.sendFeedback(Text.translatable("commands.cwaypoint.add.success", name, pos.toShortString(), dimension.getValue())); + return Command.SINGLE_SUCCESS; + } + + private static int remove(FabricClientCommandSource source, String name) throws CommandSyntaxException { + String worldIdentifier = getWorldIdentifier(source); + + 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); + } + + source.sendFeedback(Text.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().getRegistryKey()); + } + + private static int edit(FabricClientCommandSource source, String name, BlockPos pos, RegistryKey dimension) throws CommandSyntaxException { + String worldIdentifier = getWorldIdentifier(source); + + Map>> worldWaypoints = waypoints.get(worldIdentifier); + + if (worldWaypoints == null) { + throw NOT_FOUND_EXCEPTION.create(name); + } + + if (worldWaypoints.computeIfPresent(name, (key, value) -> new Pair<>(pos, dimension)) == null) { + throw NOT_FOUND_EXCEPTION.create(name); + } + + source.sendFeedback(Text.translatable("commands.cwaypoint.edit.success", name, pos.toShortString(), dimension.getValue())); + 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); + + Map>> worldWaypoints = waypoints.get(worldIdentifier); + + if (worldWaypoints.isEmpty()) { + source.sendFeedback(Text.translatable("commands.cwaypoint.list.empty")); + return Command.SINGLE_SUCCESS; + } + + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Text.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().getValue()))); + return Command.SINGLE_SUCCESS; + } + + if (waypoints.isEmpty()) { + source.sendFeedback(Text.translatable("commands.cwaypoint.list.empty")); + return Command.SINGLE_SUCCESS; + } + + waypoints.forEach((worldIdentifier, worldWaypoints) -> { + if (worldWaypoints.isEmpty()) { + return; + } + + source.sendFeedback(Text.literal(worldIdentifier).append(":")); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Text.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().getValue()))); + }); + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index 8ccf4dad3..587896b0c 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -207,6 +207,14 @@ "commands.cvar.list.empty": "No available variables", "commands.cvar.list": "Available variables: %s", + "commands.cwaypoint.add.success": "Waypoint \"%s\" at %s in %s successfully added", + "commands.cwaypoint.remove.success": "Waypoint \"%s\" successfully removed", + "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.alreadyExists": "A waypoint with the name \"%s\" already exists", + "commands.cwaypoint.notFound": "No waypoint with the name \"%s\" could be found", + "commands.cwe.playerNotFound": "Player not found", "commands.cweather.reset": "Stopped overriding weather", From a8407f49b46da0b0c66bed87bdd4b518234825be Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 28 Mar 2023 13:49:55 +0200 Subject: [PATCH 02/29] Remove unnecessary code --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 76df7194f..df71007ef 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -79,10 +79,6 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p Map>> worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); - if (worldWaypoints.containsKey(name)) { - throw ALREADY_EXISTS_EXCEPTION.create(name); - } - if (worldWaypoints.putIfAbsent(name, new Pair<>(pos, dimension)) != null) { throw ALREADY_EXISTS_EXCEPTION.create(name); } From 8fe1c583e49b883fcbddb6f90661bab69f41b7f9 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 28 Mar 2023 20:12:37 +0200 Subject: [PATCH 03/29] Implement saving to a file --- .../command/WaypointCommand.java | 68 +++++++++++++++++++ .../assets/clientcommands/lang/en_us.json | 1 + 2 files changed, 69 insertions(+) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index df71007ef..559f8534d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -4,17 +4,31 @@ 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.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.command.CommandSource; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; import net.minecraft.registry.RegistryKey; import net.minecraft.text.Text; import net.minecraft.util.Pair; +import net.minecraft.util.Util; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import org.slf4j.Logger; +import java.io.File; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; 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.*; @@ -26,9 +40,20 @@ public class WaypointCommand { public static final Map>>> waypoints = new HashMap<>(); + private static final Logger LOGGER = LogUtils.getLogger(); + + private static final SimpleCommandExceptionType SAVE_FAILED_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.cwaypoint.saveFailed")); private static final DynamicCommandExceptionType ALREADY_EXISTS_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.alreadyExists", name)); private static final DynamicCommandExceptionType NOT_FOUND_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.notFound", name)); + static { + try { + loadFile(); + } catch (IOException 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") @@ -83,6 +108,7 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p throw ALREADY_EXISTS_EXCEPTION.create(name); } + saveFile(); source.sendFeedback(Text.translatable("commands.cwaypoint.add.success", name, pos.toShortString(), dimension.getValue())); return Command.SINGLE_SUCCESS; } @@ -100,6 +126,7 @@ private static int remove(FabricClientCommandSource source, String name) throws throw NOT_FOUND_EXCEPTION.create(name); } + saveFile(); source.sendFeedback(Text.translatable("commands.cwaypoint.remove.success", name)); return Command.SINGLE_SUCCESS; } @@ -121,6 +148,7 @@ private static int edit(FabricClientCommandSource source, String name, BlockPos throw NOT_FOUND_EXCEPTION.create(name); } + saveFile(); source.sendFeedback(Text.translatable("commands.cwaypoint.edit.success", name, pos.toShortString(), dimension.getValue())); return Command.SINGLE_SUCCESS; } @@ -159,4 +187,44 @@ private static int list(FabricClientCommandSource source, boolean current) { }); return Command.SINGLE_SUCCESS; } + + private static void saveFile() throws CommandSyntaxException { + try { + NbtCompound rootTag = new NbtCompound(); + waypoints.forEach((worldIdentifier, worldWaypoints) -> rootTag.put(worldIdentifier, worldWaypoints.entrySet().stream() + .collect(NbtCompound::new, (result, entry) -> { + NbtCompound waypoint = new NbtCompound(); + NbtCompound pos = NbtHelper.fromBlockPos(entry.getValue().getLeft()); + String dimension = entry.getValue().getRight().getValue().toString(); + waypoint.put("Pos", pos); + waypoint.putString("Dimension", dimension); + result.put(entry.getKey(), waypoint); + }, NbtCompound::copyFrom))); + File newFile = File.createTempFile("waypoints", ".dat", ClientCommands.configDir.toFile()); + NbtIo.write(rootTag, newFile); + File backupFile = new File(ClientCommands.configDir.toFile(), "waypoints.dat_old"); + File currentFile = new File(ClientCommands.configDir.toFile(), "waypoints.dat"); + Util.backupAndReplace(currentFile, newFile, backupFile); + } catch (IOException e) { + throw SAVE_FAILED_EXCEPTION.create(); + } + } + + private static void loadFile() throws IOException { + waypoints.clear(); + NbtCompound rootTag = NbtIo.read(new File(ClientCommands.configDir.toFile(), "waypoints.dat")); + if (rootTag == null) { + return; + } + rootTag.getKeys().forEach(worldIdentifier -> { + NbtCompound worldWaypoints = rootTag.getCompound(worldIdentifier); + waypoints.put(worldIdentifier, worldWaypoints.getKeys().stream() + .collect(Collectors.toMap(Function.identity(), name -> { + NbtCompound waypoint = worldWaypoints.getCompound(name); + BlockPos pos = NbtHelper.toBlockPos(waypoint.getCompound("Pos")); + RegistryKey dimension = World.CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); + return new Pair<>(pos, dimension); + }))); + }); + } } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index 587896b0c..3372a3c5e 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -214,6 +214,7 @@ "commands.cwaypoint.list.empty": "No available waypoints", "commands.cwaypoint.alreadyExists": "A waypoint with the name \"%s\" already exists", "commands.cwaypoint.notFound": "No waypoint with the name \"%s\" could be found", + "commands.cwaypoint.saveFailed": "Could not save waypoints file", "commands.cwe.playerNotFound": "Player not found", From 390721b587199e3d667e061260b0f5fb69877022 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 17:06:04 +0100 Subject: [PATCH 04/29] Port to mojmap --- .../command/WaypointCommand.java | 128 +++++++++--------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 559f8534d..1297111f5 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -9,21 +9,23 @@ import com.mojang.serialization.Dynamic; import net.earthcomputer.clientcommands.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.command.CommandSource; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtHelper; +import net.minecraft.Util; +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.registry.RegistryKey; -import net.minecraft.text.Text; -import net.minecraft.util.Pair; -import net.minecraft.util.Util; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -32,19 +34,19 @@ import static com.mojang.brigadier.arguments.BoolArgumentType.*; import static com.mojang.brigadier.arguments.StringArgumentType.*; -import static dev.xpple.clientarguments.arguments.CBlockPosArgumentType.*; -import static dev.xpple.clientarguments.arguments.CDimensionArgumentType.*; +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 { - public static final Map>>> waypoints = new HashMap<>(); + public static final Map>>> waypoints = new HashMap<>(); private static final Logger LOGGER = LogUtils.getLogger(); - private static final SimpleCommandExceptionType SAVE_FAILED_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.cwaypoint.saveFailed")); - private static final DynamicCommandExceptionType ALREADY_EXISTS_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.alreadyExists", name)); - private static final DynamicCommandExceptionType NOT_FOUND_EXCEPTION = new DynamicCommandExceptionType(name -> Text.translatable("commands.cwaypoint.notFound", name)); + 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 { @@ -59,26 +61,26 @@ public static void register(CommandDispatcher dispatc .then(literal("add") .then(argument("name", word()) .then(argument("pos", blockPos()) - .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"))) + .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) .then(argument("dimension", dimension()) - .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"), getCDimensionArgument(ctx, "dimension").getRegistryKey())))))) + .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())); - return CommandSource.suggestMatching(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); + Map>> worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource())); + 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())); - return CommandSource.suggestMatching(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); + Map>> worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource())); + return SharedSuggestionProvider.suggest(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); }) .then(argument("pos", blockPos()) - .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"))) + .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) .then(argument("dimension", dimension()) - .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getCBlockPos(ctx, "pos"), getCDimensionArgument(ctx, "dimension").getRegistryKey())))))) + .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()) @@ -87,36 +89,36 @@ public static void register(CommandDispatcher dispatc private static String getWorldIdentifier(FabricClientCommandSource source) { String worldIdentifier; - if (source.getClient().isIntegratedServerRunning()) { - worldIdentifier = source.getClient().getServer().getSaveProperties().getLevelName(); + if (source.getClient().hasSingleplayerServer()) { + worldIdentifier = source.getClient().getSingleplayerServer().getWorldData().getLevelName(); } else { - worldIdentifier = source.getClient().getNetworkHandler().getConnection().getAddress().toString(); + worldIdentifier = source.getClient().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().getRegistryKey()); + return add(source, name, pos, source.getWorld().dimension()); } - private static int add(FabricClientCommandSource source, String name, BlockPos pos, RegistryKey dimension) throws CommandSyntaxException { + private static int add(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { String worldIdentifier = getWorldIdentifier(source); - Map>> worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); + Map>> worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); - if (worldWaypoints.putIfAbsent(name, new Pair<>(pos, dimension)) != null) { + if (worldWaypoints.putIfAbsent(name, Pair.of(pos, dimension)) != null) { throw ALREADY_EXISTS_EXCEPTION.create(name); } saveFile(); - source.sendFeedback(Text.translatable("commands.cwaypoint.add.success", name, pos.toShortString(), dimension.getValue())); + source.sendFeedback(Component.translatable("commands.cwaypoint.add.success", name, pos.toShortString(), dimension.location())); return Command.SINGLE_SUCCESS; } private static int remove(FabricClientCommandSource source, String name) throws CommandSyntaxException { String worldIdentifier = getWorldIdentifier(source); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map>> worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints == null) { throw NOT_FOUND_EXCEPTION.create(name); @@ -127,29 +129,29 @@ private static int remove(FabricClientCommandSource source, String name) throws } saveFile(); - source.sendFeedback(Text.translatable("commands.cwaypoint.remove.success", name)); + 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().getRegistryKey()); + return edit(source, name, pos, source.getWorld().dimension()); } - private static int edit(FabricClientCommandSource source, String name, BlockPos pos, RegistryKey dimension) throws CommandSyntaxException { + private static int edit(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { String worldIdentifier = getWorldIdentifier(source); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map>> worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints == null) { throw NOT_FOUND_EXCEPTION.create(name); } - if (worldWaypoints.computeIfPresent(name, (key, value) -> new Pair<>(pos, dimension)) == null) { + if (worldWaypoints.computeIfPresent(name, (key, value) -> Pair.of(pos, dimension)) == null) { throw NOT_FOUND_EXCEPTION.create(name); } saveFile(); - source.sendFeedback(Text.translatable("commands.cwaypoint.edit.success", name, pos.toShortString(), dimension.getValue())); + source.sendFeedback(Component.translatable("commands.cwaypoint.edit.success", name, pos.toShortString(), dimension.location())); return Command.SINGLE_SUCCESS; } @@ -161,19 +163,19 @@ private static int list(FabricClientCommandSource source, boolean current) { if (current) { String worldIdentifier = getWorldIdentifier(source); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map>> worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints.isEmpty()) { - source.sendFeedback(Text.translatable("commands.cwaypoint.list.empty")); + source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); return Command.SINGLE_SUCCESS; } - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Text.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().getValue()))); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().location()))); return Command.SINGLE_SUCCESS; } if (waypoints.isEmpty()) { - source.sendFeedback(Text.translatable("commands.cwaypoint.list.empty")); + source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); return Command.SINGLE_SUCCESS; } @@ -182,29 +184,29 @@ private static int list(FabricClientCommandSource source, boolean current) { return; } - source.sendFeedback(Text.literal(worldIdentifier).append(":")); - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Text.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().getValue()))); + source.sendFeedback(Component.literal(worldIdentifier).append(":")); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().location()))); }); return Command.SINGLE_SUCCESS; } private static void saveFile() throws CommandSyntaxException { try { - NbtCompound rootTag = new NbtCompound(); + CompoundTag rootTag = new CompoundTag(); waypoints.forEach((worldIdentifier, worldWaypoints) -> rootTag.put(worldIdentifier, worldWaypoints.entrySet().stream() - .collect(NbtCompound::new, (result, entry) -> { - NbtCompound waypoint = new NbtCompound(); - NbtCompound pos = NbtHelper.fromBlockPos(entry.getValue().getLeft()); - String dimension = entry.getValue().getRight().getValue().toString(); + .collect(CompoundTag::new, (result, entry) -> { + CompoundTag waypoint = new CompoundTag(); + Tag pos = NbtUtils.writeBlockPos(entry.getValue().getLeft()); + String dimension = entry.getValue().getRight().location().toString(); waypoint.put("Pos", pos); waypoint.putString("Dimension", dimension); result.put(entry.getKey(), waypoint); - }, NbtCompound::copyFrom))); - File newFile = File.createTempFile("waypoints", ".dat", ClientCommands.configDir.toFile()); + }, CompoundTag::merge))); + Path newFile = Files.createTempFile(ClientCommands.configDir, "waypoints", ".dat"); NbtIo.write(rootTag, newFile); - File backupFile = new File(ClientCommands.configDir.toFile(), "waypoints.dat_old"); - File currentFile = new File(ClientCommands.configDir.toFile(), "waypoints.dat"); - Util.backupAndReplace(currentFile, newFile, backupFile); + Path backupFile = ClientCommands.configDir.resolve("waypoints.dat_old"); + Path currentFile = ClientCommands.configDir.resolve("waypoints.dat"); + Util.safeReplaceFile(currentFile, newFile, backupFile); } catch (IOException e) { throw SAVE_FAILED_EXCEPTION.create(); } @@ -212,18 +214,18 @@ private static void saveFile() throws CommandSyntaxException { private static void loadFile() throws IOException { waypoints.clear(); - NbtCompound rootTag = NbtIo.read(new File(ClientCommands.configDir.toFile(), "waypoints.dat")); + CompoundTag rootTag = NbtIo.read(ClientCommands.configDir.resolve("waypoints.dat")); if (rootTag == null) { return; } - rootTag.getKeys().forEach(worldIdentifier -> { - NbtCompound worldWaypoints = rootTag.getCompound(worldIdentifier); - waypoints.put(worldIdentifier, worldWaypoints.getKeys().stream() + rootTag.getAllKeys().forEach(worldIdentifier -> { + CompoundTag worldWaypoints = rootTag.getCompound(worldIdentifier); + waypoints.put(worldIdentifier, worldWaypoints.getAllKeys().stream() .collect(Collectors.toMap(Function.identity(), name -> { - NbtCompound waypoint = worldWaypoints.getCompound(name); - BlockPos pos = NbtHelper.toBlockPos(waypoint.getCompound("Pos")); - RegistryKey dimension = World.CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); - return new Pair<>(pos, dimension); + CompoundTag waypoint = worldWaypoints.getCompound(name); + BlockPos pos = NbtUtils.readBlockPos(waypoint, "Pos").orElseThrow(); + ResourceKey dimension = Level.RESOURCE_KEY_CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); + return Pair.of(pos, dimension); }))); }); } From b28ca0cf3551d3157546f2756d9ceb9c7c22ebe9 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 19:14:47 +0100 Subject: [PATCH 05/29] Make waypoints storage future-proof + rely on level id for data persistence --- .../command/WaypointCommand.java | 19 +++++++++++++------ src/main/resources/clientcommands.aw | 3 +++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 1297111f5..69c6fef2f 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -9,6 +9,7 @@ import com.mojang.serialization.Dynamic; import net.earthcomputer.clientcommands.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.BlockPos; @@ -90,7 +91,8 @@ public static void register(CommandDispatcher dispatc private static String getWorldIdentifier(FabricClientCommandSource source) { String worldIdentifier; if (source.getClient().hasSingleplayerServer()) { - worldIdentifier = source.getClient().getSingleplayerServer().getWorldData().getLevelName(); + // the level id remains the same even after the level is renamed + worldIdentifier = source.getClient().getSingleplayerServer().storageSource.getLevelId(); } else { worldIdentifier = source.getClient().getConnection().getConnection().getRemoteAddress().toString(); } @@ -193,15 +195,18 @@ private static int list(FabricClientCommandSource source, boolean current) { private static void saveFile() throws CommandSyntaxException { try { CompoundTag rootTag = new CompoundTag(); - waypoints.forEach((worldIdentifier, worldWaypoints) -> rootTag.put(worldIdentifier, worldWaypoints.entrySet().stream() + rootTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + CompoundTag compoundTag = new CompoundTag(); + waypoints.forEach((worldIdentifier, worldWaypoints) -> compoundTag.put(worldIdentifier, worldWaypoints.entrySet().stream() .collect(CompoundTag::new, (result, entry) -> { CompoundTag waypoint = new CompoundTag(); Tag pos = NbtUtils.writeBlockPos(entry.getValue().getLeft()); + waypoint.put("pos", pos); String dimension = entry.getValue().getRight().location().toString(); - waypoint.put("Pos", pos); waypoint.putString("Dimension", dimension); result.put(entry.getKey(), waypoint); }, CompoundTag::merge))); + rootTag.put("Waypoints", compoundTag); Path newFile = Files.createTempFile(ClientCommands.configDir, "waypoints", ".dat"); NbtIo.write(rootTag, newFile); Path backupFile = ClientCommands.configDir.resolve("waypoints.dat_old"); @@ -218,12 +223,14 @@ private static void loadFile() throws IOException { if (rootTag == null) { return; } - rootTag.getAllKeys().forEach(worldIdentifier -> { - CompoundTag worldWaypoints = rootTag.getCompound(worldIdentifier); + // TODO: update-sensitive: apply custom data fixes when it becomes necessary + CompoundTag compoundTag = rootTag.getCompound("Waypoints"); + compoundTag.getAllKeys().forEach(worldIdentifier -> { + CompoundTag worldWaypoints = compoundTag.getCompound(worldIdentifier); waypoints.put(worldIdentifier, worldWaypoints.getAllKeys().stream() .collect(Collectors.toMap(Function.identity(), name -> { CompoundTag waypoint = worldWaypoints.getCompound(name); - BlockPos pos = NbtUtils.readBlockPos(waypoint, "Pos").orElseThrow(); + BlockPos pos = NbtUtils.readBlockPos(waypoint, "pos").orElseThrow(); ResourceKey dimension = Level.RESOURCE_KEY_CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); return Pair.of(pos, dimension); }))); diff --git a/src/main/resources/clientcommands.aw b/src/main/resources/clientcommands.aw index 7e78137d2..2677fc1ae 100644 --- a/src/main/resources/clientcommands.aw +++ b/src/main/resources/clientcommands.aw @@ -36,6 +36,9 @@ accessible field net/minecraft/network/codec/IdDispatchCodec toId Lit/unimi/dsi/ # 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; + # Game Options accessible field net/minecraft/client/OptionInstance value Ljava/lang/Object; From ba2361cb5c33af962fbe0f1cce55c019f9684a09 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 19:34:14 +0100 Subject: [PATCH 06/29] Fix ordering of translation keys --- src/main/resources/assets/clientcommands/lang/en_us.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index e77a4b4b4..a597d5723 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -285,12 +285,12 @@ "commands.cvar.saveFile.failed": "Could not save variables file", "commands.cwaypoint.add.success": "Waypoint \"%s\" at %s in %s successfully added", - "commands.cwaypoint.remove.success": "Waypoint \"%s\" successfully removed", + "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.alreadyExists": "A waypoint with the name \"%s\" already exists", "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", From 5cff09d5c1aa018b0da66dce0e7656745cd4cabf Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 19:35:45 +0100 Subject: [PATCH 07/29] Widen exception bound --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 69c6fef2f..2400abd6b 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -52,7 +52,7 @@ public class WaypointCommand { static { try { loadFile(); - } catch (IOException e) { + } catch (Exception e) { LOGGER.error("Could not load waypoints file, hence /cwaypoint will not work!", e); } } From 63573c3fe8fd31b8d76dd57110e72be4b4a05d35 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 19:38:31 +0100 Subject: [PATCH 08/29] Remove leading space from translation --- src/main/resources/assets/clientcommands/lang/en_us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index a597d5723..6e1d6af7f 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -287,7 +287,7 @@ "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": "- \"%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", From 14c335d36f60bc28086e1e78008f34ee5269fe16 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 7 Jan 2025 20:33:45 +0100 Subject: [PATCH 09/29] Add null check --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 2400abd6b..6de25aa8e 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -167,7 +167,7 @@ private static int list(FabricClientCommandSource source, boolean current) { Map>> worldWaypoints = waypoints.get(worldIdentifier); - if (worldWaypoints.isEmpty()) { + if (worldWaypoints == null || worldWaypoints.isEmpty()) { source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); return Command.SINGLE_SUCCESS; } From 9d5316045b00896b55dd5da1d2948eb8e46b4502 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Thu, 9 Jan 2025 22:38:14 +0100 Subject: [PATCH 10/29] Render augmented waypoint name at the top of the screen --- .../clientcommands/ClientCommands.java | 122 ++++++++++++++++++ .../command/WaypointCommand.java | 21 +-- src/main/resources/clientcommands.aw | 1 + 3 files changed, 134 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index b74ee2408..458be035f 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -1,6 +1,7 @@ // CHECKSTYLE:OFF: AvoidStarImport allow commands to be wildcard imported package net.earthcomputer.clientcommands; +import com.mojang.blaze3d.platform.Window; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.tree.LiteralCommandNode; @@ -19,18 +20,37 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.ChatFormatting; import net.minecraft.Util; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.commands.CommandBuildContext; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.apache.commons.lang3.tuple.Pair; +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.Calendar; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -91,6 +111,108 @@ public void onInitializeClient() { FishingCracker.registerEvents(); PlayerRandCracker.registerEvents(); ServerBrandManager.registerEvents(); + + HudRenderCallback.EVENT.register((guiGraphics, deltaTracker) -> { + String worldIdentifier = WaypointCommand.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); + + PriorityQueue> xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(Pair::getRight)); + waypoints.forEach((waypointName, waypoint) -> { + if (!waypoint.getRight().location().equals(minecraft.level.dimension().location())) { + return; + } + + long offset = Math.round(waypoint.getLeft().getY() - cameraEntity.position().y); + ChatFormatting colour; + String symbol; + if (offset >= 0) { + colour = ChatFormatting.GREEN; + symbol = "+"; + } + else { + colour = ChatFormatting.RED; + symbol = "-"; + } + + MutableComponent waypointComponent = Component.literal(waypointName).append(Component.literal(' ' + symbol + Math.abs(offset)).withStyle(colour)); + + Vector2d waypointLocation = new Vector2d(waypoint.getLeft().getX(), waypoint.getLeft().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(waypointComponent); + 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 waypoint ray 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()); + } + xPositionsBuilder.offer(Pair.of(waypointComponent, x)); + }); + + List> xPositions = new ArrayList<>(); + int waypointAmount = xPositionsBuilder.size(); + for (int i = 0; i < waypointAmount; i++) { + xPositions.add(xPositionsBuilder.poll()); + } + + int yOffset = 1; + Map>> positions = new HashMap<>(); + positions.put(yOffset, xPositions); + + while (true) { + List> pairs = positions.get(yOffset); + if (pairs == null) { + break; + } + int i = 0; + while (i < pairs.size() - 1) { + Pair leftPair = pairs.get(i); + Pair rightPair = pairs.get(i + 1); + Integer leftX = leftPair.getRight(); + Integer rightX = rightPair.getRight(); + int leftWidth = minecraft.font.width(leftPair.getLeft()); + int rightWidth = minecraft.font.width(rightPair.getLeft()); + if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { + List> nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); + Pair removed = pairs.remove(i + 1); + nextLevel.add(removed); + } else { + i++; + } + } + yOffset += minecraft.font.lineHeight; + } + + positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.getLeft(), waypoint.getRight(), y, 0xFFFFFF))); + }); } private static Set getCommands(CommandDispatcher dispatcher) { diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 6de25aa8e..8727ed9b6 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -11,6 +11,7 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.SharedConstants; import net.minecraft.Util; +import net.minecraft.client.Minecraft; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -68,14 +69,14 @@ public static void register(CommandDispatcher dispatc .then(literal("remove") .then(argument("name", word()) .suggests((ctx, builder) -> { - Map>> worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource())); + 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())); + Map>> worldWaypoints = waypoints.get(getWorldIdentifier(ctx.getSource().getClient())); return SharedSuggestionProvider.suggest(worldWaypoints != null ? worldWaypoints.keySet() : Collections.emptySet(), builder); }) .then(argument("pos", blockPos()) @@ -88,13 +89,13 @@ public static void register(CommandDispatcher dispatc .executes(ctx -> list(ctx.getSource(), getBool(ctx, "current")))))); } - private static String getWorldIdentifier(FabricClientCommandSource source) { + public static String getWorldIdentifier(Minecraft minecraft) { String worldIdentifier; - if (source.getClient().hasSingleplayerServer()) { + if (minecraft.hasSingleplayerServer()) { // the level id remains the same even after the level is renamed - worldIdentifier = source.getClient().getSingleplayerServer().storageSource.getLevelId(); + worldIdentifier = minecraft.getSingleplayerServer().storageSource.getLevelId(); } else { - worldIdentifier = source.getClient().getConnection().getConnection().getRemoteAddress().toString(); + worldIdentifier = minecraft.getConnection().getConnection().getRemoteAddress().toString(); } return worldIdentifier; } @@ -104,7 +105,7 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p } private static int add(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source); + String worldIdentifier = getWorldIdentifier(source.getClient()); Map>> worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); @@ -118,7 +119,7 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p } private static int remove(FabricClientCommandSource source, String name) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source); + String worldIdentifier = getWorldIdentifier(source.getClient()); Map>> worldWaypoints = waypoints.get(worldIdentifier); @@ -140,7 +141,7 @@ private static int edit(FabricClientCommandSource source, String name, BlockPos } private static int edit(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { - String worldIdentifier = getWorldIdentifier(source); + String worldIdentifier = getWorldIdentifier(source.getClient()); Map>> worldWaypoints = waypoints.get(worldIdentifier); @@ -163,7 +164,7 @@ private static int list(FabricClientCommandSource source) { private static int list(FabricClientCommandSource source, boolean current) { if (current) { - String worldIdentifier = getWorldIdentifier(source); + String worldIdentifier = getWorldIdentifier(source.getClient()); Map>> worldWaypoints = waypoints.get(worldIdentifier); diff --git a/src/main/resources/clientcommands.aw b/src/main/resources/clientcommands.aw index 2677fc1ae..1abc9abbb 100644 --- a/src/main/resources/clientcommands.aw +++ b/src/main/resources/clientcommands.aw @@ -38,6 +38,7 @@ 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; From 32605d65cf9d100d22d787153b1285f6270f5f0a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Thu, 9 Jan 2025 22:41:16 +0100 Subject: [PATCH 11/29] Move rendering code to WaypointCommand.java --- .../clientcommands/ClientCommands.java | 122 +----------------- .../command/WaypointCommand.java | 115 +++++++++++++++++ 2 files changed, 116 insertions(+), 121 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 458be035f..4cd7756bc 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -1,7 +1,6 @@ // CHECKSTYLE:OFF: AvoidStarImport allow commands to be wildcard imported package net.earthcomputer.clientcommands; -import com.mojang.blaze3d.platform.Window; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.tree.LiteralCommandNode; @@ -23,34 +22,16 @@ import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.ChatFormatting; import net.minecraft.Util; -import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GameRenderer; import net.minecraft.commands.CommandBuildContext; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.Vec3; -import org.apache.commons.lang3.tuple.Pair; -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.Calendar; -import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -111,108 +92,7 @@ public void onInitializeClient() { FishingCracker.registerEvents(); PlayerRandCracker.registerEvents(); ServerBrandManager.registerEvents(); - - HudRenderCallback.EVENT.register((guiGraphics, deltaTracker) -> { - String worldIdentifier = WaypointCommand.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); - - PriorityQueue> xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(Pair::getRight)); - waypoints.forEach((waypointName, waypoint) -> { - if (!waypoint.getRight().location().equals(minecraft.level.dimension().location())) { - return; - } - - long offset = Math.round(waypoint.getLeft().getY() - cameraEntity.position().y); - ChatFormatting colour; - String symbol; - if (offset >= 0) { - colour = ChatFormatting.GREEN; - symbol = "+"; - } - else { - colour = ChatFormatting.RED; - symbol = "-"; - } - - MutableComponent waypointComponent = Component.literal(waypointName).append(Component.literal(' ' + symbol + Math.abs(offset)).withStyle(colour)); - - Vector2d waypointLocation = new Vector2d(waypoint.getLeft().getX(), waypoint.getLeft().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(waypointComponent); - 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 waypoint ray 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()); - } - xPositionsBuilder.offer(Pair.of(waypointComponent, x)); - }); - - List> xPositions = new ArrayList<>(); - int waypointAmount = xPositionsBuilder.size(); - for (int i = 0; i < waypointAmount; i++) { - xPositions.add(xPositionsBuilder.poll()); - } - - int yOffset = 1; - Map>> positions = new HashMap<>(); - positions.put(yOffset, xPositions); - - while (true) { - List> pairs = positions.get(yOffset); - if (pairs == null) { - break; - } - int i = 0; - while (i < pairs.size() - 1) { - Pair leftPair = pairs.get(i); - Pair rightPair = pairs.get(i + 1); - Integer leftX = leftPair.getRight(); - Integer rightX = rightPair.getRight(); - int leftWidth = minecraft.font.width(leftPair.getLeft()); - int rightWidth = minecraft.font.width(rightPair.getLeft()); - if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { - List> nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); - Pair removed = pairs.remove(i + 1); - nextLevel.add(removed); - } else { - i++; - } - } - yOffset += minecraft.font.lineHeight; - } - - positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.getLeft(), waypoint.getRight(), y, 0xFFFFFF))); - }); + HudRenderCallback.EVENT.register(WaypointCommand::renderWaypoints); } private static Set getCommands(CommandDispatcher dispatcher) { diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 8727ed9b6..a86b25d84 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -1,5 +1,6 @@ package net.earthcomputer.clientcommands.command; +import com.mojang.blaze3d.platform.Window; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -9,9 +10,14 @@ import com.mojang.serialization.Dynamic; import net.earthcomputer.clientcommands.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +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.GuiGraphics; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; @@ -20,17 +26,25 @@ import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.tuple.Pair; +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.PriorityQueue; import java.util.function.Function; import java.util.stream.Collectors; @@ -237,4 +251,105 @@ private static void loadFile() throws IOException { }))); }); } + + public static void renderWaypoints(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); + + PriorityQueue> xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(Pair::getRight)); + waypoints.forEach((waypointName, waypoint) -> { + if (!waypoint.getRight().location().equals(minecraft.level.dimension().location())) { + return; + } + + long offset = Math.round(waypoint.getLeft().getY() - cameraEntity.position().y); + ChatFormatting colour; + String symbol; + if (offset >= 0) { + colour = ChatFormatting.GREEN; + symbol = "+"; + } else { + colour = ChatFormatting.RED; + symbol = "-"; + } + + MutableComponent waypointComponent = Component.literal(waypointName).append(Component.literal(' ' + symbol + Math.abs(offset)).withStyle(colour)); + + Vector2d waypointLocation = new Vector2d(waypoint.getLeft().getX(), waypoint.getLeft().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(waypointComponent); + 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 waypoint ray 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()); + } + xPositionsBuilder.offer(Pair.of(waypointComponent, x)); + }); + + List> xPositions = new ArrayList<>(); + int waypointAmount = xPositionsBuilder.size(); + for (int i = 0; i < waypointAmount; i++) { + xPositions.add(xPositionsBuilder.poll()); + } + + int yOffset = 1; + Map>> positions = new HashMap<>(); + positions.put(yOffset, xPositions); + + while (true) { + List> pairs = positions.get(yOffset); + if (pairs == null) { + break; + } + int i = 0; + while (i < pairs.size() - 1) { + Pair leftPair = pairs.get(i); + Pair rightPair = pairs.get(i + 1); + Integer leftX = leftPair.getRight(); + Integer rightX = rightPair.getRight(); + int leftWidth = minecraft.font.width(leftPair.getLeft()); + int rightWidth = minecraft.font.width(rightPair.getLeft()); + if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { + List> nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); + Pair removed = pairs.remove(i + 1); + nextLevel.add(removed); + } else { + i++; + } + } + yOffset += minecraft.font.lineHeight; + } + + positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.getLeft(), waypoint.getRight(), y, 0xFFFFFF))); + } } From 815ab4e93e8c3ee8b9a8f85e6c9685188ec1725d Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 01:00:46 +0100 Subject: [PATCH 12/29] Render box with name when waypoint is in loaded chunks --- .../clientcommands/ClientCommands.java | 4 +- .../command/WaypointCommand.java | 50 ++++++++++++++++++- .../clientcommands/render/RenderQueue.java | 2 +- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 4cd7756bc..6ace25509 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -20,6 +20,7 @@ import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.Util; @@ -92,7 +93,8 @@ public void onInitializeClient() { FishingCracker.registerEvents(); PlayerRandCracker.registerEvents(); ServerBrandManager.registerEvents(); - HudRenderCallback.EVENT.register(WaypointCommand::renderWaypoints); + HudRenderCallback.EVENT.register(WaypointCommand::renderWaypointLabels); + WorldRenderEvents.AFTER_ENTITIES.register(WaypointCommand::renderWaypointBoxes); } private static Set getCommands(CommandDispatcher dispatcher) { diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index a86b25d84..084745127 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -1,6 +1,7 @@ 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; @@ -9,15 +10,21 @@ import com.mojang.logging.LogUtils; import com.mojang.serialization.Dynamic; import net.earthcomputer.clientcommands.ClientCommands; +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.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; @@ -30,6 +37,7 @@ import net.minecraft.resources.ResourceKey; 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.apache.commons.lang3.tuple.Pair; import org.joml.Vector2d; @@ -56,7 +64,7 @@ public class WaypointCommand { - public static final Map>>> waypoints = new HashMap<>(); + private static final Map>>> waypoints = new HashMap<>(); private static final Logger LOGGER = LogUtils.getLogger(); @@ -252,7 +260,7 @@ private static void loadFile() throws IOException { }); } - public static void renderWaypoints(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { + public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); Map>> waypoints = WaypointCommand.waypoints.get(worldIdentifier); if (waypoints == null) { @@ -352,4 +360,42 @@ public static void renderWaypoints(GuiGraphics guiGraphics, DeltaTracker deltaTr positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.getLeft(), waypoint.getRight(), y, 0xFFFFFF))); } + + public 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) -> { + BlockPos waypointLocation = waypoint.getLeft(); + 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) / 4; + + PoseStack stack = context.matrixStack(); + stack.pushPose(); + stack.translate(cameraPosition.scale(-1)); + + AABB box = new AABB(waypointLocation); + ShapeRenderer.renderLineBox(stack, context.consumers().getBuffer(RenderQueue.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, 0xFFFFFF, false, stack.last().pose(), context.consumers(), Font.DisplayMode.SEE_THROUGH, backgroundColour, LightTexture.FULL_SKY); + + stack.popPose(); + }); + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/render/RenderQueue.java b/src/main/java/net/earthcomputer/clientcommands/render/RenderQueue.java index 489b3accf..f03a3e3ac 100644 --- a/src/main/java/net/earthcomputer/clientcommands/render/RenderQueue.java +++ b/src/main/java/net/earthcomputer/clientcommands/render/RenderQueue.java @@ -113,7 +113,7 @@ private record AddQueueEntry(Layer layer, Object key, Shape shape, int life) {} private record RemoveQueueEntry(Layer layer, Object key) {} - private static final RenderType NO_DEPTH_LAYER = RenderType.create("clientcommands_no_depth", DefaultVertexFormat.POSITION_COLOR_NORMAL, VertexFormat.Mode.LINES, 256, true, true, RenderType.CompositeState.builder() + public static final RenderType NO_DEPTH_LAYER = RenderType.create("clientcommands_no_depth", DefaultVertexFormat.POSITION_COLOR_NORMAL, VertexFormat.Mode.LINES, 256, true, true, RenderType.CompositeState.builder() .setShaderState(RenderType.RENDERTYPE_LINES_SHADER) .setWriteMaskState(RenderType.COLOR_WRITE) .setCullState(RenderType.NO_CULL) From 69247950b46783a0704b18a34412535e5034da19 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 01:10:19 +0100 Subject: [PATCH 13/29] Adjust size slightly --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 084745127..fbc56d506 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -377,7 +377,7 @@ public static void renderWaypointBoxes(WorldRenderContext context) { Vec3 cameraPosition = context.camera().getPosition(); float distance = (float) waypointLocation.distToCenterSqr(cameraPosition); - distance = (float) Math.sqrt(distance) / 4; + distance = (float) Math.sqrt(distance) / 6; PoseStack stack = context.matrixStack(); stack.pushPose(); From 49929fc4425f4ba5b24856032814e7b9ed672056 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 01:31:22 +0100 Subject: [PATCH 14/29] Replace y offset with distance in top screen display --- .../clientcommands/command/WaypointCommand.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index fbc56d506..3f01ea17e 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -13,7 +13,6 @@ 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.minecraft.ChatFormatting; import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.client.Camera; @@ -32,6 +31,7 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceKey; @@ -287,18 +287,10 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de return; } - long offset = Math.round(waypoint.getLeft().getY() - cameraEntity.position().y); - ChatFormatting colour; - String symbol; - if (offset >= 0) { - colour = ChatFormatting.GREEN; - symbol = "+"; - } else { - colour = ChatFormatting.RED; - symbol = "-"; - } + double distanceSquared = waypoint.getLeft().distToCenterSqr(cameraEntity.position()); + long distance = Math.round(Math.sqrt(distanceSquared)); - MutableComponent waypointComponent = Component.literal(waypointName).append(Component.literal(' ' + symbol + Math.abs(offset)).withStyle(colour)); + MutableComponent waypointComponent = Component.literal(waypointName).append(CommonComponents.space()).append(Long.toString(distance)); Vector2d waypointLocation = new Vector2d(waypoint.getLeft().getX(), waypoint.getLeft().getZ()); double angleRad = viewVector.angle(waypointLocation.sub(position, new Vector2d())); From c8a139296d5cb78b6553696b836f1ba16b29223b Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 13:49:44 +0100 Subject: [PATCH 15/29] Use records instead of pairs --- .../command/WaypointCommand.java | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 3f01ea17e..9c4b2501b 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.logging.LogUtils; import com.mojang.serialization.Dynamic; +import dev.xpple.clientarguments.arguments.CDimensionArgument; import net.earthcomputer.clientcommands.ClientCommands; import net.earthcomputer.clientcommands.render.RenderQueue; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; @@ -39,7 +40,6 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import org.apache.commons.lang3.tuple.Pair; import org.joml.Vector2d; import org.slf4j.Logger; @@ -64,7 +64,7 @@ public class WaypointCommand { - private static final Map>>> waypoints = new HashMap<>(); + private static final Map> waypoints = new HashMap<>(); private static final Logger LOGGER = LogUtils.getLogger(); @@ -86,24 +86,24 @@ public static void register(CommandDispatcher dispatc .then(argument("name", word()) .then(argument("pos", blockPos()) .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) - .then(argument("dimension", dimension()) + .then(argument("dimension", CDimensionArgument.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())); + 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())); + 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()) + .then(argument("dimension", CDimensionArgument.dimension()) .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"), getDimension(ctx, "dimension"))))))) .then(literal("list") .executes(ctx -> list(ctx.getSource())) @@ -129,9 +129,9 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p 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<>()); + Map worldWaypoints = waypoints.computeIfAbsent(worldIdentifier, key -> new HashMap<>()); - if (worldWaypoints.putIfAbsent(name, Pair.of(pos, dimension)) != null) { + if (worldWaypoints.putIfAbsent(name, new WaypointLocation(dimension, pos)) != null) { throw ALREADY_EXISTS_EXCEPTION.create(name); } @@ -143,7 +143,7 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p private static int remove(FabricClientCommandSource source, String name) throws CommandSyntaxException { String worldIdentifier = getWorldIdentifier(source.getClient()); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints == null) { throw NOT_FOUND_EXCEPTION.create(name); @@ -165,13 +165,13 @@ private static int edit(FabricClientCommandSource source, String name, BlockPos private static int edit(FabricClientCommandSource source, String name, BlockPos pos, ResourceKey dimension) throws CommandSyntaxException { String worldIdentifier = getWorldIdentifier(source.getClient()); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints == null) { throw NOT_FOUND_EXCEPTION.create(name); } - if (worldWaypoints.computeIfPresent(name, (key, value) -> Pair.of(pos, dimension)) == null) { + if (worldWaypoints.computeIfPresent(name, (key, value) -> new WaypointLocation(dimension, pos)) == null) { throw NOT_FOUND_EXCEPTION.create(name); } @@ -188,14 +188,14 @@ private static int list(FabricClientCommandSource source, boolean current) { if (current) { String worldIdentifier = getWorldIdentifier(source.getClient()); - Map>> worldWaypoints = waypoints.get(worldIdentifier); + Map worldWaypoints = waypoints.get(worldIdentifier); if (worldWaypoints == null || worldWaypoints.isEmpty()) { source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); return Command.SINGLE_SUCCESS; } - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().location()))); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.location().toShortString(), waypoint.dimension().location()))); return Command.SINGLE_SUCCESS; } @@ -210,7 +210,7 @@ private static int list(FabricClientCommandSource source, boolean current) { } source.sendFeedback(Component.literal(worldIdentifier).append(":")); - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.getLeft().toShortString(), waypoint.getRight().location()))); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.location().toShortString(), waypoint.dimension().location()))); }); return Command.SINGLE_SUCCESS; } @@ -223,9 +223,9 @@ private static void saveFile() throws CommandSyntaxException { waypoints.forEach((worldIdentifier, worldWaypoints) -> compoundTag.put(worldIdentifier, worldWaypoints.entrySet().stream() .collect(CompoundTag::new, (result, entry) -> { CompoundTag waypoint = new CompoundTag(); - Tag pos = NbtUtils.writeBlockPos(entry.getValue().getLeft()); + Tag pos = NbtUtils.writeBlockPos(entry.getValue().location()); waypoint.put("pos", pos); - String dimension = entry.getValue().getRight().location().toString(); + String dimension = entry.getValue().dimension().location().toString(); waypoint.putString("Dimension", dimension); result.put(entry.getKey(), waypoint); }, CompoundTag::merge))); @@ -255,14 +255,14 @@ private static void loadFile() throws IOException { CompoundTag waypoint = worldWaypoints.getCompound(name); BlockPos pos = NbtUtils.readBlockPos(waypoint, "pos").orElseThrow(); ResourceKey dimension = Level.RESOURCE_KEY_CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, waypoint.get("Dimension"))).resultOrPartial(LOGGER::error).orElseThrow(); - return Pair.of(pos, dimension); + return new WaypointLocation(dimension, pos); }))); }); } public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); - Map>> waypoints = WaypointCommand.waypoints.get(worldIdentifier); + Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); if (waypoints == null) { return; } @@ -281,18 +281,18 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de Vector2d viewVector = new Vector2d(viewVector3.x, viewVector3.z); Vector2d position = new Vector2d(cameraEntity.getEyePosition().x, cameraEntity.getEyePosition().z); - PriorityQueue> xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(Pair::getRight)); + PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(ComponentLocation::location)); waypoints.forEach((waypointName, waypoint) -> { - if (!waypoint.getRight().location().equals(minecraft.level.dimension().location())) { + if (!waypoint.dimension().location().equals(minecraft.level.dimension().location())) { return; } - double distanceSquared = waypoint.getLeft().distToCenterSqr(cameraEntity.position()); + double distanceSquared = waypoint.location().distToCenterSqr(cameraEntity.position()); long distance = Math.round(Math.sqrt(distanceSquared)); MutableComponent waypointComponent = Component.literal(waypointName).append(CommonComponents.space()).append(Long.toString(distance)); - Vector2d waypointLocation = new Vector2d(waypoint.getLeft().getX(), waypoint.getLeft().getZ()); + 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); @@ -313,35 +313,35 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double perc = am / ab; x = (int) (perc * guiGraphics.guiWidth()); } - xPositionsBuilder.offer(Pair.of(waypointComponent, x)); + xPositionsBuilder.offer(new ComponentLocation(waypointComponent, x)); }); - List> xPositions = new ArrayList<>(); + List xPositions = new ArrayList<>(); int waypointAmount = xPositionsBuilder.size(); for (int i = 0; i < waypointAmount; i++) { xPositions.add(xPositionsBuilder.poll()); } int yOffset = 1; - Map>> positions = new HashMap<>(); + Map> positions = new HashMap<>(); positions.put(yOffset, xPositions); while (true) { - List> pairs = positions.get(yOffset); - if (pairs == null) { + List componentLocations = positions.get(yOffset); + if (componentLocations == null) { break; } int i = 0; - while (i < pairs.size() - 1) { - Pair leftPair = pairs.get(i); - Pair rightPair = pairs.get(i + 1); - Integer leftX = leftPair.getRight(); - Integer rightX = rightPair.getRight(); - int leftWidth = minecraft.font.width(leftPair.getLeft()); - int rightWidth = minecraft.font.width(rightPair.getLeft()); + while (i < componentLocations.size() - 1) { + ComponentLocation left = componentLocations.get(i); + ComponentLocation right = componentLocations.get(i + 1); + int leftX = left.location(); + int rightX = right.location(); + int leftWidth = minecraft.font.width(left.component()); + int rightWidth = minecraft.font.width(right.component()); if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { - List> nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); - Pair removed = pairs.remove(i + 1); + List nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); + ComponentLocation removed = componentLocations.remove(i + 1); nextLevel.add(removed); } else { i++; @@ -350,19 +350,19 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de yOffset += minecraft.font.lineHeight; } - positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.getLeft(), waypoint.getRight(), y, 0xFFFFFF))); + positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.component(), waypoint.location(), y, 0xFFFFFF))); } public static void renderWaypointBoxes(WorldRenderContext context) { String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); - Map>> waypoints = WaypointCommand.waypoints.get(worldIdentifier); + Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); if (waypoints == null) { return; } ClientChunkCache chunkSource = context.world().getChunkSource(); waypoints.forEach((waypointName, waypoint) -> { - BlockPos waypointLocation = waypoint.getLeft(); + BlockPos waypointLocation = waypoint.location(); if (!chunkSource.hasChunk(waypointLocation.getX() >> 4, waypointLocation.getZ() >> 4)) { return; } @@ -390,4 +390,10 @@ public static void renderWaypointBoxes(WorldRenderContext context) { stack.popPose(); }); } + + record WaypointLocation(ResourceKey dimension, BlockPos location) { + } + + record ComponentLocation(Component component, int location) { + } } From 650c7faca96b491c48b144ae24731adedde2973a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 13:51:23 +0100 Subject: [PATCH 16/29] No idea how this happened --- .../clientcommands/command/WaypointCommand.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 9c4b2501b..3861d37de 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -9,7 +9,6 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.logging.LogUtils; import com.mojang.serialization.Dynamic; -import dev.xpple.clientarguments.arguments.CDimensionArgument; import net.earthcomputer.clientcommands.ClientCommands; import net.earthcomputer.clientcommands.render.RenderQueue; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; @@ -86,7 +85,7 @@ public static void register(CommandDispatcher dispatc .then(argument("name", word()) .then(argument("pos", blockPos()) .executes(ctx -> add(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) - .then(argument("dimension", CDimensionArgument.dimension()) + .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()) @@ -103,7 +102,7 @@ public static void register(CommandDispatcher dispatc }) .then(argument("pos", blockPos()) .executes(ctx -> edit(ctx.getSource(), getString(ctx, "name"), getBlockPos(ctx, "pos"))) - .then(argument("dimension", CDimensionArgument.dimension()) + .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())) From 78b48fb7a5014b2fdb172e796cb41583b87e8a91 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 14:17:00 +0100 Subject: [PATCH 17/29] Replace map with list --- .../command/WaypointCommand.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 3861d37de..d52237e95 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -321,15 +321,11 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de xPositions.add(xPositionsBuilder.poll()); } - int yOffset = 1; - Map> positions = new HashMap<>(); - positions.put(yOffset, xPositions); - - while (true) { - List componentLocations = positions.get(yOffset); - if (componentLocations == null) { - break; - } + List> positions = new ArrayList<>(); + positions.add(xPositions); + + for (int line = 0; line < positions.size(); line++) { + List componentLocations = positions.get(line); int i = 0; while (i < componentLocations.size() - 1) { ComponentLocation left = componentLocations.get(i); @@ -339,17 +335,24 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de int leftWidth = minecraft.font.width(left.component()); int rightWidth = minecraft.font.width(right.component()); if (leftWidth / 2 + rightWidth / 2 > rightX - leftX) { - List nextLevel = positions.computeIfAbsent(yOffset + minecraft.font.lineHeight, k -> new ArrayList<>()); + if (line + 1 == positions.size()) { + positions.add(new ArrayList<>()); + } + List nextLevel = positions.get(line + 1); ComponentLocation removed = componentLocations.remove(i + 1); nextLevel.add(removed); } else { i++; } } - yOffset += minecraft.font.lineHeight; } - positions.forEach((y, w) -> w.forEach(waypoint -> guiGraphics.drawCenteredString(minecraft.font, waypoint.component(), waypoint.location(), y, 0xFFFFFF))); + for (int line = 0; line < positions.size(); line++) { + List w = positions.get(line); + for (ComponentLocation waypoint : w) { + guiGraphics.drawCenteredString(minecraft.font, waypoint.component(), waypoint.location(), 1 + line * minecraft.font.lineHeight, 0xFFFFFF); + } + } } public static void renderWaypointBoxes(WorldRenderContext context) { From 2d882a8318a525f7d20fb441243ed3f3f0ad6e8a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 14:19:03 +0100 Subject: [PATCH 18/29] Make comment clearer --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index d52237e95..cf80d8581 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -304,7 +304,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de // V is the view vector // A is the leftmost visible direction // B is the rightmost visible direction - // M is the intersection of the waypoint ray with AB + // 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; From 895112e79d32954f222c4456063b62fe5adb8ffb Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 14:38:48 +0100 Subject: [PATCH 19/29] Sort waypoint lines based on distance --- .../command/WaypointCommand.java | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index cf80d8581..934fc36c7 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -31,9 +31,7 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; -import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; @@ -280,7 +278,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de Vector2d viewVector = new Vector2d(viewVector3.x, viewVector3.z); Vector2d position = new Vector2d(cameraEntity.getEyePosition().x, cameraEntity.getEyePosition().z); - PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(ComponentLocation::location)); + PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(WaypointLabelLocationDistance::location)); waypoints.forEach((waypointName, waypoint) -> { if (!waypoint.dimension().location().equals(minecraft.level.dimension().location())) { return; @@ -288,8 +286,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double distanceSquared = waypoint.location().distToCenterSqr(cameraEntity.position()); long distance = Math.round(Math.sqrt(distanceSquared)); - - MutableComponent waypointComponent = Component.literal(waypointName).append(CommonComponents.space()).append(Long.toString(distance)); + String label = waypointName + ' ' + distance; Vector2d waypointLocation = new Vector2d(waypoint.location().getX(), waypoint.location().getZ()); double angleRad = viewVector.angle(waypointLocation.sub(position, new Vector2d())); @@ -298,7 +295,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de int x; if (angleRad > horizontalFovRad / 2) { - int width = minecraft.font.width(waypointComponent); + int width = minecraft.font.width(label); x = right ? guiGraphics.guiWidth() - width / 2 : width / 2; } else { // V is the view vector @@ -312,34 +309,35 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double perc = am / ab; x = (int) (perc * guiGraphics.guiWidth()); } - xPositionsBuilder.offer(new ComponentLocation(waypointComponent, x)); + xPositionsBuilder.offer(new WaypointLabelLocationDistance(label, x, distance)); }); - List xPositions = new ArrayList<>(); + List xPositions = new ArrayList<>(); int waypointAmount = xPositionsBuilder.size(); for (int i = 0; i < waypointAmount; i++) { xPositions.add(xPositionsBuilder.poll()); } - List> positions = new ArrayList<>(); + List> positions = new ArrayList<>(); positions.add(xPositions); for (int line = 0; line < positions.size(); line++) { - List componentLocations = positions.get(line); + List waypointLabelLocationDistances = positions.get(line); int i = 0; - while (i < componentLocations.size() - 1) { - ComponentLocation left = componentLocations.get(i); - ComponentLocation right = componentLocations.get(i + 1); + while (i < waypointLabelLocationDistances.size() - 1) { + WaypointLabelLocationDistance left = waypointLabelLocationDistances.get(i); + WaypointLabelLocationDistance right = waypointLabelLocationDistances.get(i + 1); int leftX = left.location(); int rightX = right.location(); - int leftWidth = minecraft.font.width(left.component()); - int rightWidth = minecraft.font.width(right.component()); + 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); - ComponentLocation removed = componentLocations.remove(i + 1); + List nextLevel = positions.get(line + 1); + int idx = left.distance() > right.distance() ? i + 1 : i; + WaypointLabelLocationDistance removed = waypointLabelLocationDistances.remove(idx); nextLevel.add(removed); } else { i++; @@ -348,9 +346,9 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de } for (int line = 0; line < positions.size(); line++) { - List w = positions.get(line); - for (ComponentLocation waypoint : w) { - guiGraphics.drawCenteredString(minecraft.font, waypoint.component(), waypoint.location(), 1 + line * minecraft.font.lineHeight, 0xFFFFFF); + List w = positions.get(line); + for (WaypointLabelLocationDistance waypoint : w) { + guiGraphics.drawCenteredString(minecraft.font, waypoint.label(), waypoint.location(), 1 + line * minecraft.font.lineHeight, 0xFFFFFF); } } } @@ -396,6 +394,6 @@ public static void renderWaypointBoxes(WorldRenderContext context) { record WaypointLocation(ResourceKey dimension, BlockPos location) { } - record ComponentLocation(Component component, int location) { + record WaypointLabelLocationDistance(String label, int location, long distance) { } } From cd3bba831b0e254e777121163cae976fee1d046a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 14:43:19 +0100 Subject: [PATCH 20/29] Undo sorting because it did not work --- .../command/WaypointCommand.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 934fc36c7..969073754 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -278,7 +278,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de Vector2d viewVector = new Vector2d(viewVector3.x, viewVector3.z); Vector2d position = new Vector2d(cameraEntity.getEyePosition().x, cameraEntity.getEyePosition().z); - PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(WaypointLabelLocationDistance::location)); + PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(WaypointLabelLocation::location)); waypoints.forEach((waypointName, waypoint) -> { if (!waypoint.dimension().location().equals(minecraft.level.dimension().location())) { return; @@ -309,24 +309,24 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double perc = am / ab; x = (int) (perc * guiGraphics.guiWidth()); } - xPositionsBuilder.offer(new WaypointLabelLocationDistance(label, x, distance)); + xPositionsBuilder.offer(new WaypointLabelLocation(label, x)); }); - List xPositions = new ArrayList<>(); + List xPositions = new ArrayList<>(); int waypointAmount = xPositionsBuilder.size(); for (int i = 0; i < waypointAmount; i++) { xPositions.add(xPositionsBuilder.poll()); } - List> positions = new ArrayList<>(); + List> positions = new ArrayList<>(); positions.add(xPositions); for (int line = 0; line < positions.size(); line++) { - List waypointLabelLocationDistances = positions.get(line); + List waypointLabelLocations = positions.get(line); int i = 0; - while (i < waypointLabelLocationDistances.size() - 1) { - WaypointLabelLocationDistance left = waypointLabelLocationDistances.get(i); - WaypointLabelLocationDistance right = waypointLabelLocationDistances.get(i + 1); + 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()); @@ -335,9 +335,8 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de if (line + 1 == positions.size()) { positions.add(new ArrayList<>()); } - List nextLevel = positions.get(line + 1); - int idx = left.distance() > right.distance() ? i + 1 : i; - WaypointLabelLocationDistance removed = waypointLabelLocationDistances.remove(idx); + List nextLevel = positions.get(line + 1); + WaypointLabelLocation removed = waypointLabelLocations.remove(i + 1); nextLevel.add(removed); } else { i++; @@ -346,8 +345,8 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de } for (int line = 0; line < positions.size(); line++) { - List w = positions.get(line); - for (WaypointLabelLocationDistance waypoint : w) { + List w = positions.get(line); + for (WaypointLabelLocation waypoint : w) { guiGraphics.drawCenteredString(minecraft.font, waypoint.label(), waypoint.location(), 1 + line * minecraft.font.lineHeight, 0xFFFFFF); } } @@ -394,6 +393,6 @@ public static void renderWaypointBoxes(WorldRenderContext context) { record WaypointLocation(ResourceKey dimension, BlockPos location) { } - record WaypointLabelLocationDistance(String label, int location, long distance) { + record WaypointLabelLocation(String label, int location) { } } From ad9efd2cd4102e1210d0bee530ecaa76cdf5f329 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 14:59:55 +0100 Subject: [PATCH 21/29] Bracket labels and make them yellow --- .../clientcommands/command/WaypointCommand.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 969073754..04aafe1d0 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -13,6 +13,7 @@ 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.minecraft.ChatFormatting; import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.client.Camera; @@ -32,6 +33,7 @@ import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; import net.minecraft.resources.ResourceKey; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; @@ -286,7 +288,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double distanceSquared = waypoint.location().distToCenterSqr(cameraEntity.position()); long distance = Math.round(Math.sqrt(distanceSquared)); - String label = waypointName + ' ' + distance; + 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())); @@ -393,6 +395,6 @@ public static void renderWaypointBoxes(WorldRenderContext context) { record WaypointLocation(ResourceKey dimension, BlockPos location) { } - record WaypointLabelLocation(String label, int location) { + record WaypointLabelLocation(Component label, int location) { } } From 016d21f7eb36fca76ca14d634c86efa31138cb5a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 15:04:00 +0100 Subject: [PATCH 22/29] Add dimension check for waypoint boxes --- .../clientcommands/command/WaypointCommand.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 04aafe1d0..6c357c3fd 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -110,7 +110,7 @@ public static void register(CommandDispatcher dispatc .executes(ctx -> list(ctx.getSource(), getBool(ctx, "current")))))); } - public static String getWorldIdentifier(Minecraft minecraft) { + private static String getWorldIdentifier(Minecraft minecraft) { String worldIdentifier; if (minecraft.hasSingleplayerServer()) { // the level id remains the same even after the level is renamed @@ -363,6 +363,10 @@ public static void renderWaypointBoxes(WorldRenderContext context) { 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; From 70cfd256fb95a23cf3cdc1fc5f9fc19c4b80d709 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 15:20:45 +0100 Subject: [PATCH 23/29] Format coordinates nicely --- .../command/WaypointCommand.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 6c357c3fd..f8a54b539 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -32,8 +32,10 @@ import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; +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.world.entity.Entity; import net.minecraft.world.level.Level; @@ -135,7 +137,7 @@ private static int add(FabricClientCommandSource source, String name, BlockPos p } saveFile(); - source.sendFeedback(Component.translatable("commands.cwaypoint.add.success", name, pos.toShortString(), dimension.location())); + source.sendFeedback(Component.translatable("commands.cwaypoint.add.success", name, formatCoordinates(pos), dimension.location())); return Command.SINGLE_SUCCESS; } @@ -175,7 +177,7 @@ private static int edit(FabricClientCommandSource source, String name, BlockPos } saveFile(); - source.sendFeedback(Component.translatable("commands.cwaypoint.edit.success", name, pos.toShortString(), dimension.location())); + source.sendFeedback(Component.translatable("commands.cwaypoint.edit.success", name, formatCoordinates(pos), dimension.location())); return Command.SINGLE_SUCCESS; } @@ -194,7 +196,9 @@ private static int list(FabricClientCommandSource source, boolean current) { return Command.SINGLE_SUCCESS; } - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.location().toShortString(), waypoint.dimension().location()))); + + + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, formatCoordinates(waypoint.location()), waypoint.dimension().location()))); return Command.SINGLE_SUCCESS; } @@ -209,7 +213,7 @@ private static int list(FabricClientCommandSource source, boolean current) { } source.sendFeedback(Component.literal(worldIdentifier).append(":")); - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, waypoint.location().toShortString(), waypoint.dimension().location()))); + worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, formatCoordinates(waypoint.location()), waypoint.dimension().location()))); }); return Command.SINGLE_SUCCESS; } @@ -259,6 +263,14 @@ private static void loadFile() throws IOException { }); } + private static Component formatCoordinates(BlockPos waypoint) { + return ComponentUtils.wrapInSquareBrackets(Component.literal(waypoint.toShortString())).withStyle(style -> style + .withColor(ChatFormatting.GREEN) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, waypoint.getX() + " " + waypoint.getY() + " " + waypoint.getZ())) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("chat.copy.click"))) + ); + } + public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); From 136b320c7a219e7db04eca710491f61283e505d0 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 10 Jan 2025 17:34:05 +0100 Subject: [PATCH 24/29] Widen exception bound in throws clause --- .../earthcomputer/clientcommands/command/WaypointCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index f8a54b539..e9e3f8394 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -243,7 +243,7 @@ private static void saveFile() throws CommandSyntaxException { } } - private static void loadFile() throws IOException { + private static void loadFile() throws Exception { waypoints.clear(); CompoundTag rootTag = NbtIo.read(ClientCommands.configDir.resolve("waypoints.dat")); if (rootTag == null) { From 7a6f86675934cd64694c87ec8017bd7d731ed31f Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 17 Jan 2025 17:29:10 +0100 Subject: [PATCH 25/29] Replace priority queue with list --- gradle.properties | 2 +- .../clientcommands/command/WaypointCommand.java | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index c247dd425..e7c339f0d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ org.gradle.jvmargs=-Xmx2G # also check this on https://fabricmc.net/develop/ fabric_version=0.110.5+1.21.4 clientarguments_version=1.10.1 - betterconfig_version=2.1.2 + betterconfig_version=2.3.0 seedfinding_core_version=1.200.1 seedfinding_biome_version=1.171.1 seedfinding_feature_version=1.171.9 diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index e9e3f8394..1e11f4e79 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -53,7 +53,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.PriorityQueue; import java.util.function.Function; import java.util.stream.Collectors; @@ -292,7 +291,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de Vector2d viewVector = new Vector2d(viewVector3.x, viewVector3.z); Vector2d position = new Vector2d(cameraEntity.getEyePosition().x, cameraEntity.getEyePosition().z); - PriorityQueue xPositionsBuilder = new PriorityQueue<>(Comparator.comparingInt(WaypointLabelLocation::location)); + List xPositions = new ArrayList<>(); waypoints.forEach((waypointName, waypoint) -> { if (!waypoint.dimension().location().equals(minecraft.level.dimension().location())) { return; @@ -323,14 +322,10 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de double perc = am / ab; x = (int) (perc * guiGraphics.guiWidth()); } - xPositionsBuilder.offer(new WaypointLabelLocation(label, x)); + xPositions.add(new WaypointLabelLocation(label, x)); }); - List xPositions = new ArrayList<>(); - int waypointAmount = xPositionsBuilder.size(); - for (int i = 0; i < waypointAmount; i++) { - xPositions.add(xPositionsBuilder.poll()); - } + xPositions.sort(Comparator.comparingInt(WaypointLabelLocation::location)); List> positions = new ArrayList<>(); positions.add(xPositions); From 79b0386145958f31158df3a8b1a3e07c13836343 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 17 Jan 2025 17:43:14 +0100 Subject: [PATCH 26/29] Return amount of waypoints in list subcommand --- .../clientcommands/command/WaypointCommand.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 1e11f4e79..c9c188dee 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -53,6 +53,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -192,29 +193,30 @@ private static int list(FabricClientCommandSource source, boolean current) { if (worldWaypoints == null || worldWaypoints.isEmpty()) { source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); - return Command.SINGLE_SUCCESS; + return 0; } - - worldWaypoints.forEach((name, waypoint) -> source.sendFeedback(Component.translatable("commands.cwaypoint.list", name, formatCoordinates(waypoint.location()), waypoint.dimension().location()))); - return Command.SINGLE_SUCCESS; + return worldWaypoints.size(); } if (waypoints.isEmpty()) { source.sendFeedback(Component.translatable("commands.cwaypoint.list.empty")); - return Command.SINGLE_SUCCESS; + return 0; } + AtomicInteger count = new AtomicInteger(); waypoints.forEach((worldIdentifier, worldWaypoints) -> { if (worldWaypoints.isEmpty()) { return; } + count.addAndGet(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 Command.SINGLE_SUCCESS; + return count.get(); } private static void saveFile() throws CommandSyntaxException { From bdac0281fd5b1511201f6972a262ee6b03686343 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 17 Jan 2025 21:07:17 +0100 Subject: [PATCH 27/29] Replace AtomicInteger with singleton array --- .../clientcommands/command/WaypointCommand.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index c9c188dee..5a0718f1d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -53,7 +53,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -205,18 +204,18 @@ private static int list(FabricClientCommandSource source, boolean current) { return 0; } - AtomicInteger count = new AtomicInteger(); + int[] count = {0}; waypoints.forEach((worldIdentifier, worldWaypoints) -> { if (worldWaypoints.isEmpty()) { return; } - count.addAndGet(worldWaypoints.size()); + 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.get(); + return count[0]; } private static void saveFile() throws CommandSyntaxException { From 8d8353849c9cb3453779d0b0620bc15604f66c00 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Fri, 17 Jan 2025 21:09:43 +0100 Subject: [PATCH 28/29] Introduce WaypointCommand.registerEvents method --- .../earthcomputer/clientcommands/ClientCommands.java | 5 +---- .../clientcommands/command/WaypointCommand.java | 11 +++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 6ace25509..d73c0c74a 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -19,8 +19,6 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.Util; @@ -93,8 +91,7 @@ public void onInitializeClient() { FishingCracker.registerEvents(); PlayerRandCracker.registerEvents(); ServerBrandManager.registerEvents(); - HudRenderCallback.EVENT.register(WaypointCommand::renderWaypointLabels); - WorldRenderEvents.AFTER_ENTITIES.register(WaypointCommand::renderWaypointBoxes); + WaypointCommand.registerEvents(); } private static Set getCommands(CommandDispatcher dispatcher) { diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 5a0718f1d..9932e2ea2 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -12,7 +12,9 @@ import net.earthcomputer.clientcommands.ClientCommands; import net.earthcomputer.clientcommands.render.RenderQueue; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.minecraft.ChatFormatting; import net.minecraft.SharedConstants; import net.minecraft.Util; @@ -271,7 +273,12 @@ private static Component formatCoordinates(BlockPos waypoint) { ); } - public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { + public static void registerEvents() { + HudRenderCallback.EVENT.register(WaypointCommand::renderWaypointLabels); + WorldRenderEvents.AFTER_ENTITIES.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) { @@ -362,7 +369,7 @@ public static void renderWaypointLabels(GuiGraphics guiGraphics, DeltaTracker de } } - public static void renderWaypointBoxes(WorldRenderContext context) { + private static void renderWaypointBoxes(WorldRenderContext context) { String worldIdentifier = getWorldIdentifier(Minecraft.getInstance()); Map waypoints = WaypointCommand.waypoints.get(worldIdentifier); if (waypoints == null) { From 0ab199eca53fab56f2daaf5dda71cce885814567 Mon Sep 17 00:00:00 2001 From: joe Date: Fri, 17 Jan 2025 22:07:13 +0000 Subject: [PATCH 29/29] Add unit test for loading waypoint file --- .../clientcommands/ClientCommands.java | 41 +++++++------ .../command/WaypointCommand.java | 22 +++++-- .../features/ClientCommandFunctions.java | 2 +- .../mixin/scrambletitle/MinecraftMixin.java | 2 +- .../clientcommands/util/DebugRandom.java | 2 +- .../clientcommands/util/MappingsHelper.java | 2 +- .../test/WaypointLoadingTest.java | 58 +++++++++++++++++++ 7 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index d73c0c74a..db9a9b4e4 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -21,7 +21,6 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.commands.CommandBuildContext; import org.slf4j.Logger; @@ -37,25 +36,11 @@ public class ClientCommands implements ClientModInitializer { private static final Logger LOGGER = LogUtils.getLogger(); - public static Path configDir; + public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve("clientcommands"); private static final Set clientcommandsCommands = new HashSet<>(); private static final Set COMMANDS_TO_NOT_SEND_TO_SERVER = Set.of("cwe", "cnote"); // could contain private information - public static final boolean SCRAMBLE_WINDOW_TITLE = Util.make(() -> { - String playerUUID = String.valueOf(Minecraft.getInstance().getUser().getProfileId()); - - Set victims = Set.of( - "fa68270b-1071-46c6-ac5c-6c4a0b777a96", // Earthcomputer - "d4557649-e553-413e-a019-56d14548df96", // Azteched - "8dc3d945-cf90-47c1-a122-a576319d05a7", // samnrad - "c5d72740-cabc-42d1-b789-27859041d553", // allocator - "e4093360-a200-4f99-aa13-be420b8d9a79", // Rybot666 - "083fb87e-c9e4-4489-8fb7-a45b06bfca90", // Kerbaras - "973e8f6e-2f51-4307-97dc-56fdc71d194f" // KatieTheQt - ); - - return victims.contains(playerUUID) || Boolean.getBoolean("clientcommands.scrambleWindowTitle"); - }); + public static boolean scrambleWindowTitle = false; private static final Set CHAT_COMMAND_USERS = Set.of( "b793c3b9-425f-4dd8-a056-9dec4d835e24", // wsb @@ -65,10 +50,11 @@ public class ClientCommands implements ClientModInitializer { @Override public void onInitializeClient() { + setupScrambleWindowTitle(); + // Config - configDir = FabricLoader.getInstance().getConfigDir().resolve("clientcommands"); try { - Files.createDirectories(configDir); + Files.createDirectories(CONFIG_DIR); } catch (IOException e) { LOGGER.error("Failed to create config dir", e); } @@ -94,6 +80,23 @@ public void onInitializeClient() { WaypointCommand.registerEvents(); } + private static void setupScrambleWindowTitle() { + // can't set this up during class initializer, because Minecraft.getInstance() is null during automated tests + String playerUUID = String.valueOf(Minecraft.getInstance().getUser().getProfileId()); + + Set victims = Set.of( + "fa68270b-1071-46c6-ac5c-6c4a0b777a96", // Earthcomputer + "d4557649-e553-413e-a019-56d14548df96", // Azteched + "8dc3d945-cf90-47c1-a122-a576319d05a7", // samnrad + "c5d72740-cabc-42d1-b789-27859041d553", // allocator + "e4093360-a200-4f99-aa13-be420b8d9a79", // Rybot666 + "083fb87e-c9e4-4489-8fb7-a45b06bfca90", // Kerbaras + "973e8f6e-2f51-4307-97dc-56fdc71d194f" // KatieTheQt + ); + + scrambleWindowTitle = victims.contains(playerUUID) || Boolean.getBoolean("clientcommands.scrambleWindowTitle"); + } + private static Set getCommands(CommandDispatcher dispatcher) { return dispatcher.getRoot().getChildren().stream().flatMap(node -> node instanceof LiteralCommandNode literal ? Stream.of(literal.getLiteral()) : Stream.empty()).collect(Collectors.toSet()); } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java index 9932e2ea2..e44b249c4 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WaypointCommand.java @@ -43,6 +43,7 @@ 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; @@ -235,10 +236,10 @@ private static void saveFile() throws CommandSyntaxException { result.put(entry.getKey(), waypoint); }, CompoundTag::merge))); rootTag.put("Waypoints", compoundTag); - Path newFile = Files.createTempFile(ClientCommands.configDir, "waypoints", ".dat"); + Path newFile = Files.createTempFile(ClientCommands.CONFIG_DIR, "waypoints", ".dat"); NbtIo.write(rootTag, newFile); - Path backupFile = ClientCommands.configDir.resolve("waypoints.dat_old"); - Path currentFile = ClientCommands.configDir.resolve("waypoints.dat"); + 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(); @@ -247,11 +248,17 @@ private static void saveFile() throws CommandSyntaxException { private static void loadFile() throws Exception { waypoints.clear(); - CompoundTag rootTag = NbtIo.read(ClientCommands.configDir.resolve("waypoints.dat")); + CompoundTag rootTag = NbtIo.read(ClientCommands.CONFIG_DIR.resolve("waypoints.dat")); if (rootTag == null) { return; } - // TODO: update-sensitive: apply custom data fixes when it becomes necessary + waypoints.putAll(deserializeWaypoints(rootTag)); + } + + @VisibleForTesting + public static Map> deserializeWaypoints(CompoundTag rootTag) { + Map> waypoints = new HashMap<>(); + CompoundTag compoundTag = rootTag.getCompound("Waypoints"); compoundTag.getAllKeys().forEach(worldIdentifier -> { CompoundTag worldWaypoints = compoundTag.getCompound(worldIdentifier); @@ -263,6 +270,8 @@ private static void loadFile() throws Exception { return new WaypointLocation(dimension, pos); }))); }); + + return waypoints; } private static Component formatCoordinates(BlockPos waypoint) { @@ -411,7 +420,8 @@ private static void renderWaypointBoxes(WorldRenderContext context) { }); } - record WaypointLocation(ResourceKey dimension, BlockPos location) { + @VisibleForTesting + public record WaypointLocation(ResourceKey dimension, BlockPos location) { } record WaypointLabelLocation(Component label, int location) { diff --git a/src/main/java/net/earthcomputer/clientcommands/features/ClientCommandFunctions.java b/src/main/java/net/earthcomputer/clientcommands/features/ClientCommandFunctions.java index 796730a62..4a18ff968 100644 --- a/src/main/java/net/earthcomputer/clientcommands/features/ClientCommandFunctions.java +++ b/src/main/java/net/earthcomputer/clientcommands/features/ClientCommandFunctions.java @@ -40,7 +40,7 @@ public class ClientCommandFunctions { private static final Logger LOGGER = LogUtils.getLogger(); - private static final Path FUNCTION_DIR = ClientCommands.configDir.resolve("functions"); + private static final Path FUNCTION_DIR = ClientCommands.CONFIG_DIR.resolve("functions"); private static final DynamicCommandExceptionType NO_SUCH_FUNCTION_EXCEPTION = new DynamicCommandExceptionType(id -> Component.translatable("arguments.function.unknown", id)); private static final DynamicCommandExceptionType COMMAND_LIMIT_REACHED_EXCEPTION = new DynamicCommandExceptionType(limit -> Component.translatable("commands.cfunction.limitReached", limit)); diff --git a/src/main/java/net/earthcomputer/clientcommands/mixin/scrambletitle/MinecraftMixin.java b/src/main/java/net/earthcomputer/clientcommands/mixin/scrambletitle/MinecraftMixin.java index 525dd2263..6ccd88d13 100644 --- a/src/main/java/net/earthcomputer/clientcommands/mixin/scrambletitle/MinecraftMixin.java +++ b/src/main/java/net/earthcomputer/clientcommands/mixin/scrambletitle/MinecraftMixin.java @@ -17,7 +17,7 @@ public class MinecraftMixin { // Earth annoying his friends <3 nothing to see here @Inject(method = "createTitle", at = @At("RETURN"), cancellable = true) private void modifyWindowTitle(CallbackInfoReturnable ci) { - if (ClientCommands.SCRAMBLE_WINDOW_TITLE) { + if (ClientCommands.scrambleWindowTitle) { List chars = ci.getReturnValue().chars().mapToObj(c -> (char) c).collect(Collectors.toCollection(ArrayList::new)); Collections.shuffle(chars); ci.setReturnValue(chars.stream().map(String::valueOf).collect(Collectors.joining())); diff --git a/src/main/java/net/earthcomputer/clientcommands/util/DebugRandom.java b/src/main/java/net/earthcomputer/clientcommands/util/DebugRandom.java index bf14fd9ff..67a74f69c 100644 --- a/src/main/java/net/earthcomputer/clientcommands/util/DebugRandom.java +++ b/src/main/java/net/earthcomputer/clientcommands/util/DebugRandom.java @@ -116,7 +116,7 @@ private void handleStackTrace(int stackTrace) { public void writeToFile() { try { this.nbtStream.close(); - Path debugDir = ClientCommands.configDir.resolve("debug"); + Path debugDir = ClientCommands.CONFIG_DIR.resolve("debug"); Files.createDirectories(debugDir); try (DataOutputStream dataOutput = new DataOutputStream(new GZIPOutputStream(Files.newOutputStream(debugDir.resolve(this.entity.getStringUUID() + ".dat"))))) { dataOutput.writeInt(stackTraceById.size()); diff --git a/src/main/java/net/earthcomputer/clientcommands/util/MappingsHelper.java b/src/main/java/net/earthcomputer/clientcommands/util/MappingsHelper.java index 018a10be6..b676472c6 100644 --- a/src/main/java/net/earthcomputer/clientcommands/util/MappingsHelper.java +++ b/src/main/java/net/earthcomputer/clientcommands/util/MappingsHelper.java @@ -46,7 +46,7 @@ public static void load() { private static final Logger LOGGER = LogUtils.getLogger(); - private static final Path MAPPINGS_DIR = ClientCommands.configDir.resolve("mappings"); + private static final Path MAPPINGS_DIR = ClientCommands.CONFIG_DIR.resolve("mappings"); private static final boolean IS_DEV_ENV = FabricLoader.getInstance().isDevelopmentEnvironment(); diff --git a/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java b/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java new file mode 100644 index 000000000..b2ce5f056 --- /dev/null +++ b/src/test/java/net/earthcomputer/clientcommands/test/WaypointLoadingTest.java @@ -0,0 +1,58 @@ +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 new TagParser(new StringReader(snbt)).readStruct(); + } 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()); + } +}