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()); + } +}