diff --git a/src/main/java/com/glisco/isometricrenders/command/IsorenderCommand.java b/src/main/java/com/glisco/isometricrenders/command/IsorenderCommand.java index f48e3bb..74120c2 100644 --- a/src/main/java/com/glisco/isometricrenders/command/IsorenderCommand.java +++ b/src/main/java/com/glisco/isometricrenders/command/IsorenderCommand.java @@ -8,8 +8,11 @@ import com.glisco.isometricrenders.screen.RenderScreen; import com.glisco.isometricrenders.screen.ScreenScheduler; import com.glisco.isometricrenders.util.AreaSelectionHelper; +import com.glisco.isometricrenders.util.InstantRenderer; +import com.glisco.isometricrenders.util.OutputPathBuilder; import com.glisco.isometricrenders.util.Translate; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -22,6 +25,7 @@ import net.minecraft.command.CommandSource; import net.minecraft.command.argument.*; import net.minecraft.entity.EntityType; +import net.minecraft.entity.decoration.ItemFrameEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; @@ -78,8 +82,22 @@ public static void register(CommandDispatcher dispatc .executes(IsorenderCommand::renderPlayerWithNbt)))) .then(literal("item") .executes(IsorenderCommand::renderHeldItem) + .then(literal("selection") + .then(argument("folder", StringArgumentType.string()) + .then(argument("name", StringArgumentType.string()) + .executes(IsorenderCommand::renderItemWithNbtAndOutput)) + ) + .then(argument("folder", StringArgumentType.string()) + .then(argument("name", StringArgumentType.string()) + .then(argument("silent", BoolArgumentType.bool()) + .executes(IsorenderCommand::renderItemWithNbtAndOutput)) + ))) .then(argument("item", ItemStackArgumentType.itemStack(access)) - .executes(IsorenderCommand::renderItemWithArgument))) + .executes(IsorenderCommand::renderItemWithArgument) + .then(argument("folder", StringArgumentType.string()) + .then(argument("name", StringArgumentType.string()) + .executes(IsorenderCommand::renderItemWithArgumentAndOutput)) + ))) .then(literal("tooltip") .executes(IsorenderCommand::renderHeldItemTooltip) .then(argument("item", ItemStackArgumentType.itemStack(access)) @@ -219,6 +237,56 @@ private static int renderItemWithArgument(CommandContext context) { + boolean renderSilent; + try { + renderSilent = BoolArgumentType.getBool(context, "silent"); + } catch (Exception e) { + renderSilent = false; + } + if (renderSilent) { + InstantRenderer.render(renderable); + } else { + ScreenScheduler.schedule(new RenderScreen(renderable)); + } + } + + private static int renderItemWithNbtAndOutput(CommandContext context) throws CommandSyntaxException { + ItemStack item = getItemToRender(); + ItemRenderable renderable = new ItemRenderable(item); + OutputPathBuilder builder = new OutputPathBuilder(renderable, context); + String result = builder.appendToRenderable(); + if (result != null) { + return 1; + } + render(renderable, context); + return 0; + } + + private static int renderItemWithArgumentAndOutput(CommandContext context) throws CommandSyntaxException { + ItemRenderable renderable = new ItemRenderable(ItemStackArgumentType.getItemStackArgument(context, "item").createStack(1, false)); + OutputPathBuilder builder = new OutputPathBuilder(renderable, context); + builder.appendToRenderable(); + render(renderable, context); + return 0; + } + private static int renderHeldItem(CommandContext context) { ScreenScheduler.schedule(new RenderScreen( new ItemRenderable(MinecraftClient.getInstance().player.getMainHandStack()) diff --git a/src/main/java/com/glisco/isometricrenders/render/ItemRenderable.java b/src/main/java/com/glisco/isometricrenders/render/ItemRenderable.java index 6d27390..82a4d1d 100644 --- a/src/main/java/com/glisco/isometricrenders/render/ItemRenderable.java +++ b/src/main/java/com/glisco/isometricrenders/render/ItemRenderable.java @@ -22,9 +22,14 @@ public class ItemRenderable extends DefaultRenderable { } private final ItemStack stack; + private ExportPathSpec exportPath; public ItemRenderable(ItemStack stack) { this.stack = stack; + this.exportPath = ExportPathSpec.ofIdentified( + Registries.ITEM.getId(this.stack.getItem()), + "item" + ); } @Override @@ -58,9 +63,14 @@ public DefaultPropertyBundle properties() { @Override public ExportPathSpec exportPath() { - return ExportPathSpec.ofIdentified( - Registries.ITEM.getId(this.stack.getItem()), - "item" - ); + return this.exportPath; + } + + public ItemStack stack(){ + return this.stack; + } + + public void exportPath(String rootOffset, String path){ + this.exportPath = ExportPathSpec.forced(rootOffset, path); } } diff --git a/src/main/java/com/glisco/isometricrenders/util/InstantRenderer.java b/src/main/java/com/glisco/isometricrenders/util/InstantRenderer.java new file mode 100644 index 0000000..95f110d --- /dev/null +++ b/src/main/java/com/glisco/isometricrenders/util/InstantRenderer.java @@ -0,0 +1,15 @@ +package com.glisco.isometricrenders.util; + +import com.glisco.isometricrenders.render.Renderable; +import com.glisco.isometricrenders.render.RenderableDispatcher; + +import static com.glisco.isometricrenders.property.GlobalProperties.exportResolution; + +public class InstantRenderer { + public static void render(Renderable renderable){ + ImageIO.save( + RenderableDispatcher.drawIntoImage(renderable, 0, exportResolution), + renderable.exportPath() + ); + } +} diff --git a/src/main/java/com/glisco/isometricrenders/util/OutputPathBuilder.java b/src/main/java/com/glisco/isometricrenders/util/OutputPathBuilder.java new file mode 100644 index 0000000..0b9c0f7 --- /dev/null +++ b/src/main/java/com/glisco/isometricrenders/util/OutputPathBuilder.java @@ -0,0 +1,97 @@ +package com.glisco.isometricrenders.util; + +import com.glisco.isometricrenders.render.ItemRenderable; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OutputPathBuilder { + private final ItemRenderable renderable; + private final String rawFolder; + private final String rawName; + + public OutputPathBuilder(ItemRenderable renderable, CommandContext context){ + this.renderable = renderable; + this.rawFolder = StringArgumentType.getString(context, "folder").replaceAll("\\.", "/"); + this.rawName = StringArgumentType.getString(context, "name"); + } + + public ItemRenderable renderable(){ + return renderable; + } + + @Nullable + public String appendToRenderable(){ + String nbtData = renderable.stack().getNbt() != null ? renderable.stack().getNbt().asString() : "{}"; + + Pattern pattern = Pattern.compile("\\{NBT:([a-zA-Z0-9.\\[\\]]+)\\}"); + + try{ + String name = replaceNBTPlaceholders(rawName, nbtData, pattern); + renderable.exportPath(rawFolder, name); + return null; + }catch (IllegalArgumentException iae){ + return iae.getMessage(); + } + } + + private static String replaceNBTPlaceholders(String s, String nbtData, Pattern pattern) { + Matcher matcher = pattern.matcher(s); + + while (matcher.find()) { + String fullPath = matcher.group(0); + String path = matcher.group(1); + String value = getValueFromNBTByPath(nbtData, path); + s = s.replace(fullPath, value); + } + + return s; + } + + private static String getValueFromNBTByPath(String nbtJson, String path) { + JsonElement je = JsonParser.parseString(nbtJson); + + for (String part : path.split("\\.")) { + + if (part.matches(".+\\[\\d+\\]")) { + + int leftBracketIndex = part.indexOf("["); + int rightBracketIndex = part.indexOf("]"); + + if (je.isJsonObject() && je.getAsJsonObject().has(part.substring(0, leftBracketIndex))) { + je = je.getAsJsonObject().get(part.substring(0, leftBracketIndex)); // get the array + } else { + throw new IllegalArgumentException("No such field exists in the nbt data: " + part); + } + + int index; + try { + index = Integer.parseInt(part.substring(leftBracketIndex + 1, rightBracketIndex)); // get index + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid index value for nbt part: " + part); + } + + if (je.isJsonArray() && je.getAsJsonArray().size() > index) { + je = je.getAsJsonArray().get(index); // get value at index + } else { + throw new IllegalArgumentException("Array index out of bound for nbt part: " + part); + } + + } else { + if (je.isJsonObject() && je.getAsJsonObject().has(part)) { + je = je.getAsJsonObject().get(part); + } else { + throw new IllegalArgumentException("No such field exists in the nbt data: " + part); + } + } + } + + return je.getAsString(); + } +}