From c83c1f6a19ec7fcc574239434de4ceb8364a21af Mon Sep 17 00:00:00 2001 From: haykam821 <24855774+haykam821@users.noreply.github.com> Date: Sun, 16 Apr 2023 12:04:20 -0400 Subject: [PATCH] Add the dump command --- .../nucleoid/creator_tools/CreatorTools.java | 16 ++++ .../creator_tools/command/DumpCommand.java | 59 +++++++++++++ .../command/MapManageCommand.java | 14 +--- .../creator_tools/exporter/DumpExporter.java | 84 +++++++++++++++++++ .../{ => exporter}/MapTemplateExporter.java | 3 +- .../nucleoid_creator_tools/lang/en_us.json | 2 + 6 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 src/main/java/xyz/nucleoid/creator_tools/command/DumpCommand.java create mode 100644 src/main/java/xyz/nucleoid/creator_tools/exporter/DumpExporter.java rename src/main/java/xyz/nucleoid/creator_tools/{ => exporter}/MapTemplateExporter.java (95%) diff --git a/src/main/java/xyz/nucleoid/creator_tools/CreatorTools.java b/src/main/java/xyz/nucleoid/creator_tools/CreatorTools.java index 56b818b..e4ff151 100644 --- a/src/main/java/xyz/nucleoid/creator_tools/CreatorTools.java +++ b/src/main/java/xyz/nucleoid/creator_tools/CreatorTools.java @@ -7,7 +7,9 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.Identifier; +import xyz.nucleoid.creator_tools.command.DumpCommand; import xyz.nucleoid.creator_tools.command.MapManageCommand; import xyz.nucleoid.creator_tools.command.MapMetadataCommand; import xyz.nucleoid.creator_tools.item.CreatorToolsItems; @@ -15,6 +17,8 @@ import xyz.nucleoid.creator_tools.workspace.WorkspaceTraveler; import xyz.nucleoid.creator_tools.workspace.editor.WorkspaceNetworking; +import java.util.Locale; + public final class CreatorTools implements ModInitializer { public static final String ID = "nucleoid_creator_tools"; @@ -27,6 +31,7 @@ public void onInitialize() { CommandRegistrationCallback.EVENT.register((dispatcher, dedicated, environment) -> { MapManageCommand.register(dispatcher); MapMetadataCommand.register(dispatcher); + DumpCommand.register(dispatcher); }); ServerTickEvents.START_SERVER_TICK.register(server -> { @@ -42,4 +47,15 @@ public void onInitialize() { public static Identifier identifier(String path) { return new Identifier(ID, path); } + + public static Identifier getSourceNameIdentifier(ServerCommandSource source, Identifier identifier) { + if (identifier.getNamespace().equals("minecraft")) { + var sourceName = source.getName() + .toLowerCase(Locale.ROOT) + .replaceAll("\\s", "_"); + return new Identifier(sourceName, identifier.getPath()); + } + + return identifier; + } } diff --git a/src/main/java/xyz/nucleoid/creator_tools/command/DumpCommand.java b/src/main/java/xyz/nucleoid/creator_tools/command/DumpCommand.java new file mode 100644 index 0000000..b5d4cbe --- /dev/null +++ b/src/main/java/xyz/nucleoid/creator_tools/command/DumpCommand.java @@ -0,0 +1,59 @@ +package xyz.nucleoid.creator_tools.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.command.argument.IdentifierArgumentType; +import net.minecraft.server.command.DataCommand; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.command.DataCommand.ObjectType; +import net.minecraft.text.Text; +import xyz.nucleoid.creator_tools.CreatorTools; +import xyz.nucleoid.creator_tools.exporter.DumpExporter; + +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +public final class DumpCommand { + // @formatter:off + public static void register(CommandDispatcher dispatcher) { + var builder = literal("dump").requires(source -> source.hasPermissionLevel(4)); + + for (var objectType : DataCommand.TARGET_OBJECT_TYPES) { + objectType.addArgumentsToBuilder(builder, builderx -> { + return builderx + .then(argument("path", IdentifierArgumentType.identifier()) + .executes(context -> DumpCommand.dump(context, objectType))); + }); + } + + dispatcher.register(builder); + } + // @formatter:on + + private static int dump(CommandContext context, ObjectType objectType) throws CommandSyntaxException { + var source = context.getSource(); + + var object = objectType.getObject(context); + var nbt = object.getNbt(); + + var givenIdentifier = IdentifierArgumentType.getIdentifier(context, "path"); + var identifier = CreatorTools.getSourceNameIdentifier(source, givenIdentifier); + + var future = DumpExporter.saveToExport(source.getServer(), nbt, identifier); + + future.handle((v, throwable) -> { + if (throwable == null) { + source.sendFeedback(Text.translatable("text.nucleoid_creator_tools.dump.success", identifier), false); + } else { + CreatorTools.LOGGER.error("Failed to export object to '{}'", identifier, throwable); + source.sendError(Text.translatable("text.nucleoid_creator_tools.dump.error")); + } + return null; + }); + + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/xyz/nucleoid/creator_tools/command/MapManageCommand.java b/src/main/java/xyz/nucleoid/creator_tools/command/MapManageCommand.java index de9cc90..3e98d6c 100644 --- a/src/main/java/xyz/nucleoid/creator_tools/command/MapManageCommand.java +++ b/src/main/java/xyz/nucleoid/creator_tools/command/MapManageCommand.java @@ -26,7 +26,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.dimension.DimensionTypes; import xyz.nucleoid.creator_tools.CreatorTools; -import xyz.nucleoid.creator_tools.MapTemplateExporter; +import xyz.nucleoid.creator_tools.exporter.MapTemplateExporter; import xyz.nucleoid.creator_tools.workspace.MapWorkspaceManager; import xyz.nucleoid.creator_tools.workspace.WorkspaceTraveler; import xyz.nucleoid.fantasy.RuntimeWorldConfig; @@ -36,7 +36,6 @@ import xyz.nucleoid.map_templates.MapTemplateSerializer; import java.io.IOException; -import java.util.Locale; import java.util.concurrent.CompletableFuture; import static net.minecraft.server.command.CommandManager.argument; @@ -125,16 +124,7 @@ private static int openWorkspace(CommandContext context, Ru var source = context.getSource(); var givenIdentifier = IdentifierArgumentType.getIdentifier(context, "workspace"); - - Identifier identifier; - if (givenIdentifier.getNamespace().equals("minecraft")) { - var sourceName = context.getSource().getName() - .toLowerCase(Locale.ROOT) - .replaceAll("\\s", "_"); - identifier = new Identifier(sourceName, givenIdentifier.getPath()); - } else { - identifier = givenIdentifier; - } + var identifier = CreatorTools.getSourceNameIdentifier(source, givenIdentifier); var workspaceManager = MapWorkspaceManager.get(source.getServer()); if (workspaceManager.byId(identifier) != null) { diff --git a/src/main/java/xyz/nucleoid/creator_tools/exporter/DumpExporter.java b/src/main/java/xyz/nucleoid/creator_tools/exporter/DumpExporter.java new file mode 100644 index 0000000..09d5ffc --- /dev/null +++ b/src/main/java/xyz/nucleoid/creator_tools/exporter/DumpExporter.java @@ -0,0 +1,84 @@ +package xyz.nucleoid.creator_tools.exporter; + +import net.minecraft.data.DataProvider; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtOps; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.InvalidIdentifierException; +import net.minecraft.util.JsonHelper; +import net.minecraft.util.PathUtil; +import net.minecraft.util.Util; +import net.minecraft.util.WorldSavePath; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import com.google.gson.stream.JsonWriter; +import com.mojang.serialization.JsonOps; + +public final class DumpExporter { + private static final String DATA_DIRECTORY = "data"; + private static final String FILE_EXTENSION = ".json"; + + private DumpExporter() { + } + + public static CompletableFuture saveToExport(MinecraftServer server, NbtCompound nbt, Identifier identifier) { + return CompletableFuture.supplyAsync(() -> { + try { + var generatedPath = server.getSavePath(WorldSavePath.GENERATED); + var path = getAndCheckDataPath(generatedPath, identifier, FILE_EXTENSION); + + var json = NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, nbt); + System.out.println(json); + + Files.createDirectories(path.getParent()); + + try (var output = Files.newOutputStream(path)) { + var jsonWriter = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)); + + jsonWriter.setSerializeNulls(false); + jsonWriter.setIndent(" "); + + JsonHelper.writeSorted(jsonWriter, json, DataProvider.JSON_KEY_SORTING_COMPARATOR); + jsonWriter.flush(); + } + + return null; + } catch (IOException | InvalidIdentifierException e) { + throw new CompletionException(e); + } + }, Util.getIoWorkerExecutor()); + } + + public static Path getDataPath(Path path, Identifier identifier, String extension) { + try { + Path namespacePath = path.resolve(identifier.getNamespace()); + Path dataPath = namespacePath.resolve(DATA_DIRECTORY); + + return PathUtil.getResourcePath(dataPath, identifier.getPath(), extension); + } catch (InvalidPathException e) { + throw new InvalidIdentifierException("Invalid resource path: " + identifier, e); + } + } + + private static Path getAndCheckDataPath(Path path, Identifier identifier, String extension) { + if (identifier.getPath().contains("//")) { + throw new InvalidIdentifierException("Invalid resource path: " + identifier); + } + + Path dataPath = getDataPath(path, identifier, extension); + if (!(dataPath.startsWith(path) && PathUtil.isNormal(dataPath) && PathUtil.isAllowedName(dataPath))) { + throw new InvalidIdentifierException("Invalid resource path: " + dataPath); + } + + return dataPath; + } +} diff --git a/src/main/java/xyz/nucleoid/creator_tools/MapTemplateExporter.java b/src/main/java/xyz/nucleoid/creator_tools/exporter/MapTemplateExporter.java similarity index 95% rename from src/main/java/xyz/nucleoid/creator_tools/MapTemplateExporter.java rename to src/main/java/xyz/nucleoid/creator_tools/exporter/MapTemplateExporter.java index 5b0b3d1..ab98556 100644 --- a/src/main/java/xyz/nucleoid/creator_tools/MapTemplateExporter.java +++ b/src/main/java/xyz/nucleoid/creator_tools/exporter/MapTemplateExporter.java @@ -1,7 +1,8 @@ -package xyz.nucleoid.creator_tools; +package xyz.nucleoid.creator_tools.exporter; import net.minecraft.util.Identifier; import net.minecraft.util.Util; +import xyz.nucleoid.creator_tools.CreatorTools; import xyz.nucleoid.map_templates.MapTemplate; import xyz.nucleoid.map_templates.MapTemplateSerializer; diff --git a/src/main/resources/data/nucleoid_creator_tools/lang/en_us.json b/src/main/resources/data/nucleoid_creator_tools/lang/en_us.json index 62dec1a..a5ed5d9 100644 --- a/src/main/resources/data/nucleoid_creator_tools/lang/en_us.json +++ b/src/main/resources/data/nucleoid_creator_tools/lang/en_us.json @@ -15,6 +15,8 @@ "item.nucleoid_creator_tools.region_visibility_filter.set_filter": "Set the region visibility filter", "text.nucleoid_creator_tools.chunk_generator.generator_not_found": "Chunk generator with id '%s' was not found!", "text.nucleoid_creator_tools.dimension_options.dimension_not_found": "Dimension with id '%s' was not found!", + "text.nucleoid_creator_tools.dump.error": "Failed to dump data! An unexpected exception was thrown", + "text.nucleoid_creator_tools.dump.success": "Dumped data for '%s'", "text.nucleoid_creator_tools.map_workspace.workspace_not_found": "Map with id '%s' was not found!", "text.nucleoid_creator_tools.map.bounds.get": "The bounds for the workspace are %s to %s", "text.nucleoid_creator_tools.map.bounds.set": "Updated bounds for workspace",