1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-20 15:29:27 +00:00

Work on problem reporting

This commit is contained in:
Eclipse
2025-07-02 09:09:06 +00:00
parent c8fc9541c0
commit 1458949446
5 changed files with 145 additions and 70 deletions

View File

@@ -27,6 +27,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
private final List<String> pendingPackCommands = new ArrayList<>(); private final List<String> pendingPackCommands = new ArrayList<>();
private boolean waitingOnItem = false; private boolean waitingOnItem = false;
private boolean waitingOnClear = false;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
@@ -50,7 +51,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
.then(ClientCommandManager.literal("map") .then(ClientCommandManager.literal("map")
.executes(context -> { .executes(context -> {
ItemStack heldItem = context.getSource().getPlayer().getMainHandItem(); ItemStack heldItem = context.getSource().getPlayer().getMainHandItem();
PackManager.getInstance().map(heldItem, true); PackManager.getInstance().map(heldItem);
context.getSource().sendFeedback(Component.literal("Added held item to Geyser mappings")); context.getSource().sendFeedback(Component.literal("Added held item to Geyser mappings"));
return 0; return 0;
}) })
@@ -59,7 +60,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
.executes(context -> { .executes(context -> {
int mapped = 0; int mapped = 0;
for (ItemStack stack : context.getSource().getPlayer().getInventory()) { for (ItemStack stack : context.getSource().getPlayer().getInventory()) {
if (PackManager.getInstance().map(stack, false)) { if (PackManager.getInstance().map(stack)) {
mapped++; mapped++;
} }
} }
@@ -69,10 +70,8 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
) )
.then(ClientCommandManager.literal("finish") .then(ClientCommandManager.literal("finish")
.executes(context -> { .executes(context -> {
try { if (!PackManager.getInstance().finish()) {
PackManager.getInstance().finish(); throw new SimpleCommandExceptionType(Component.literal("Errors occurred whilst trying to write the pack to disk!")).create();
} catch (IOException exception) {
throw new SimpleCommandExceptionType(Component.literal(exception.getMessage())).create();
} }
context.getSource().sendFeedback(Component.literal("Wrote pack to disk")); context.getSource().sendFeedback(Component.literal("Wrote pack to disk"));
return 0; return 0;
@@ -80,8 +79,10 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
) )
.then(ClientCommandManager.literal("auto") .then(ClientCommandManager.literal("auto")
.then(ClientCommandManager.argument("namespace", StringArgumentType.word()) .then(ClientCommandManager.argument("namespace", StringArgumentType.word())
.then(ClientCommandManager.argument("path", StringArgumentType.string())
.executes(context -> { .executes(context -> {
String namespace = StringArgumentType.getString(context, "namespace"); String namespace = StringArgumentType.getString(context, "namespace");
String path = StringArgumentType.getString(context, "path");
// Duplicated code, this is just to try this out // Duplicated code, this is just to try this out
try { try {
@@ -93,7 +94,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
// hack // hack
CommandContext<?> suggestionsContext = new CommandContext<>(null, CommandContext<?> suggestionsContext = new CommandContext<>(null,
"loot give @s loot " + namespace + ":", "loot give @s loot " + namespace + ":" + path,
null, null, null, null, null, null, null, false); null, null, null, null, null, null, null, false);
context.getSource().getClient().getConnection().getSuggestionsProvider() context.getSource().getClient().getConnection().getSuggestionsProvider()
.customSuggestion(suggestionsContext) .customSuggestion(suggestionsContext)
@@ -102,12 +103,15 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
}) })
) )
) )
)
); );
}); });
ClientTickEvents.START_CLIENT_TICK.register(client -> { ClientTickEvents.START_CLIENT_TICK.register(client -> {
if (!pendingPackCommands.isEmpty() || waitingOnItem) { if (!pendingPackCommands.isEmpty() || waitingOnItem || waitingOnClear) {
if (!waitingOnItem) { if (waitingOnClear && client.player.getInventory().isEmpty()) {
waitingOnClear = false;
} else if (!waitingOnItem) {
String command = pendingPackCommands.removeFirst(); String command = pendingPackCommands.removeFirst();
client.getConnection().send(new ServerboundChatCommandPacket("loot give @s loot " + command)); client.getConnection().send(new ServerboundChatCommandPacket("loot give @s loot " + command));
waitingOnItem = true; waitingOnItem = true;
@@ -116,7 +120,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
int mapped = 0; int mapped = 0;
for (ItemStack stack : client.player.getInventory()) { for (ItemStack stack : client.player.getInventory()) {
try { try {
if (PackManager.getInstance().map(stack, false)) { if (PackManager.getInstance().map(stack)) {
mapped++; mapped++;
} }
} catch (Exception exception) { } catch (Exception exception) {
@@ -130,11 +134,16 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
waitingOnItem = false; waitingOnItem = false;
if (pendingPackCommands.isEmpty()) { if (pendingPackCommands.isEmpty()) {
try { try {
PackManager.getInstance().finish(); if (!PackManager.getInstance().finish()) {
} catch (IOException | CommandSyntaxException exception) { client.player.displayClientMessage(Component.literal("Errors occurred whilst trying to write the pack to disk!"), false);
throw new RuntimeException(exception); return;
}
} catch (CommandSyntaxException e) {
throw new RuntimeException(e);
} }
client.player.displayClientMessage(Component.literal("Wrote pack to disk"), false); client.player.displayClientMessage(Component.literal("Wrote pack to disk"), false);
} else {
waitingOnClear = true;
} }
} }
} }

View File

@@ -24,25 +24,17 @@ public final class PackManager {
currentPack = new BedrockPack(name); currentPack = new BedrockPack(name);
} }
public boolean map(ItemStack stack, boolean throwOnModelMissing) throws CommandSyntaxException { public boolean map(ItemStack stack) throws CommandSyntaxException {
ensurePackIsCreated(); ensurePackIsCreated();
try { return currentPack.map(stack);
currentPack.map(stack);
return true;
} catch (IllegalArgumentException exception) {
if (throwOnModelMissing) {
throw new SimpleCommandExceptionType(Component.literal("Item stack does not have a custom model")).create();
} else {
return false;
}
}
} }
public void finish() throws CommandSyntaxException, IOException { public boolean finish() throws CommandSyntaxException {
ensurePackIsCreated(); ensurePackIsCreated();
currentPack.save(); boolean success = currentPack.save();
currentPack = null; currentPack = null;
return success;
} }
private void ensurePackIsCreated() throws CommandSyntaxException { private void ensurePackIsCreated() throws CommandSyntaxException {

View File

@@ -19,6 +19,7 @@ import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty
import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.item.CrossbowItem; import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.equipment.trim.TrimMaterial; import net.minecraft.world.item.equipment.trim.TrimMaterial;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -37,9 +38,10 @@ import java.util.stream.Stream;
public class GeyserItemMapper { public class GeyserItemMapper {
public static Stream<GeyserMapping> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch) { public static Stream<GeyserMapping> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch,
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch); ProblemReporter reporter) {
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation); ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch, reporter.forChild(() -> "model " + modelLocation + " "));
return mapItem(model, context); return mapItem(model, context);
} }
@@ -53,14 +55,15 @@ public class GeyserItemMapper {
return Stream.of(context.create(itemModel)); return Stream.of(context.create(itemModel));
} }
case ConditionalItemModel conditional -> { case ConditionalItemModel conditional -> {
return mapConditionalModel(conditional, context); return mapConditionalModel(conditional, context.child("condition " + conditional + " "));
} }
case SelectItemModel<?> select -> { case SelectItemModel<?> select -> {
return mapSelectModel(select, context); return mapSelectModel(select, context.child("select " + select + " "));
} }
default -> {} default -> {}
} }
throw new UnsupportedOperationException("Unable to map item model " + model.getClass()); context.reporter.report(() -> "unable to map item model " + model.getClass());
return Stream.empty();
} }
private static Stream<GeyserMapping> mapConditionalModel(ConditionalItemModel model, MappingContext context) { private static Stream<GeyserMapping> mapConditionalModel(ConditionalItemModel model, MappingContext context) {
@@ -71,15 +74,19 @@ public class GeyserItemMapper {
case CustomModelDataProperty customModelData -> new GeyserConditionPredicate.CustomModelData(customModelData.index()); case CustomModelDataProperty customModelData -> new GeyserConditionPredicate.CustomModelData(customModelData.index());
case HasComponent hasComponent -> new GeyserConditionPredicate.HasComponent(hasComponent.componentType()); // ignoreDefault property not a thing, we should look into that in Geyser! TODO case HasComponent hasComponent -> new GeyserConditionPredicate.HasComponent(hasComponent.componentType()); // ignoreDefault property not a thing, we should look into that in Geyser! TODO
case FishingRodCast ignored -> GeyserConditionPredicate.FISHING_ROD_CAST; case FishingRodCast ignored -> GeyserConditionPredicate.FISHING_ROD_CAST;
default -> throw new UnsupportedOperationException("Unsupported conditional model property " + property.getClass()); default -> null;
}; };
if (predicateProperty == null) {
context.reporter.report(() -> "unsupported conditional model property " + property);
return Stream.empty();
}
ItemModel onTrue = ((ConditionalItemModelAccessor) model).getOnTrue(); ItemModel onTrue = ((ConditionalItemModelAccessor) model).getOnTrue();
ItemModel onFalse = ((ConditionalItemModelAccessor) model).getOnFalse(); ItemModel onFalse = ((ConditionalItemModelAccessor) model).getOnFalse();
return Stream.concat( return Stream.concat(
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true))), mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true), "condition on true ")),
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false))) mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false), "condition on false "))
); );
} }
@@ -92,22 +99,32 @@ public class GeyserItemMapper {
case ContextDimension ignored -> dimension -> new GeyserMatchPredicate.ContextDimension((ResourceKey<Level>) dimension); case ContextDimension ignored -> dimension -> new GeyserMatchPredicate.ContextDimension((ResourceKey<Level>) dimension);
// Why, Mojang? // Why, Mojang?
case net.minecraft.client.renderer.item.properties.select.CustomModelDataProperty customModelData -> string -> new GeyserMatchPredicate.CustomModelData((String) string, customModelData.index()); case net.minecraft.client.renderer.item.properties.select.CustomModelDataProperty customModelData -> string -> new GeyserMatchPredicate.CustomModelData((String) string, customModelData.index());
default -> throw new UnsupportedOperationException("Unsupported select model property " + property.getClass()); default -> null;
}; };
if (dataConstructor == null) {
context.reporter.report(() -> "unsupported select model property " + property);
return Stream.empty();
}
//noinspection unchecked //noinspection unchecked
Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).geyser_mappings_generator$getCases(); Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).geyser_mappings_generator$getCases();
return Stream.concat( return Stream.concat(
cases.entrySet().stream() cases.entrySet().stream()
.flatMap(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey()))))), .flatMap(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey())), "select case " + caze.getKey() + " "))),
mapItem(cases.defaultReturnValue(), context) mapItem(cases.defaultReturnValue(), context.child("default case "))
); );
} }
private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch) { private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch,
ProblemReporter reporter) {
public MappingContext with(GeyserPredicate predicate) { public MappingContext with(GeyserPredicate predicate, String childName) {
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch); return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch,
reporter.forChild(() -> childName));
}
public MappingContext child(String childName) {
return new MappingContext(predicateStack, model, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName));
} }
public GeyserMapping create(ResourceLocation bedrockIdentifier) { public GeyserMapping create(ResourceLocation bedrockIdentifier) {

View File

@@ -7,6 +7,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.geysermc.packgenerator.CodecUtil; import org.geysermc.packgenerator.CodecUtil;
@@ -48,20 +49,25 @@ public class GeyserMappings {
mappings.put(item, mapping); mappings.put(item, mapping);
} }
public void map(ItemStack stack, Consumer<GeyserMapping> mappingConsumer) { public void map(ItemStack stack, ProblemReporter reporter, Consumer<GeyserMapping> mappingConsumer) {
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL); Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
//noinspection OptionalAssignedToNull - annoying Mojang //noinspection OptionalAssignedToNull - annoying Mojang
if (patchedModel == null || patchedModel.isEmpty()) { if (patchedModel == null || patchedModel.isEmpty()) {
throw new IllegalArgumentException("Item stack does not have a custom model"); return;
} }
ResourceLocation model = patchedModel.get(); ResourceLocation model = patchedModel.get();
String displayName = stack.getHoverName().getString(); String displayName = stack.getHoverName().getString();
int protectionValue = 0; // TODO check the attributes int protectionValue = 0; // TODO check the attributes
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch()) GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter)
.forEach(mapping -> { .forEach(mapping -> {
try {
map(stack.getItemHolder(), mapping); map(stack.getItemHolder(), mapping);
} catch (IllegalArgumentException exception) {
reporter.forChild(() -> "mapping with bedrock identifier " + mapping.bedrockIdentifier() + " ").report(() -> "failed to add mapping to mappings file: " + exception.getMessage());
return;
}
mappingConsumer.accept(mapping); mappingConsumer.accept(mapping);
}); });
} }

View File

@@ -3,6 +3,7 @@ package org.geysermc.packgenerator.pack;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.geysermc.packgenerator.CodecUtil; import org.geysermc.packgenerator.CodecUtil;
@@ -10,16 +11,19 @@ import org.geysermc.packgenerator.PackConstants;
import org.geysermc.packgenerator.mapping.attachable.AttachableMapper; import org.geysermc.packgenerator.mapping.attachable.AttachableMapper;
import org.geysermc.packgenerator.mapping.geyser.GeyserMappings; import org.geysermc.packgenerator.mapping.geyser.GeyserMappings;
import org.geysermc.packgenerator.pack.attachable.BedrockAttachable; import org.geysermc.packgenerator.pack.attachable.BedrockAttachable;
import org.jetbrains.annotations.NotNull;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
public class BedrockPack { public class BedrockPack {
private static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir().resolve("geyser"); private static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir().resolve("geyser");
@@ -30,6 +34,8 @@ public class BedrockPack {
private static final Path MANIFEST_FILE = Path.of("manifest.json"); private static final Path MANIFEST_FILE = Path.of("manifest.json");
private static final Path ITEM_ATLAS_FILE = Path.of("textures/item_texture.json"); private static final Path ITEM_ATLAS_FILE = Path.of("textures/item_texture.json");
private static final Path REPORT_FILE = Path.of("report.txt");
private final String name; private final String name;
private final Path exportPath; private final Path exportPath;
private final Path packPath; private final Path packPath;
@@ -39,22 +45,45 @@ public class BedrockPack {
private final List<BedrockAttachable> attachables = new ArrayList<>(); private final List<BedrockAttachable> attachables = new ArrayList<>();
private final List<ResourceLocation> texturesToExport = new ArrayList<>(); private final List<ResourceLocation> texturesToExport = new ArrayList<>();
private final ProblemReporter.Collector reporter;
public BedrockPack(String name) throws IOException { public BedrockPack(String name) throws IOException {
this.name = name; this.name = name;
exportPath = createPackDirectory(name); exportPath = createPackDirectory(name);
packPath = exportPath.resolve(PACK_DIRECTORY); packPath = exportPath.resolve(PACK_DIRECTORY);
mappings = CodecUtil.readOrCompute(GeyserMappings.CODEC, exportPath.resolve(MAPPINGS_FILE), GeyserMappings::new); mappings = CodecUtil.readOrCompute(GeyserMappings.CODEC, exportPath.resolve(MAPPINGS_FILE), GeyserMappings::new);
manifest = CodecUtil.readOrCompute(PackManifest.CODEC, packPath.resolve(MANIFEST_FILE), () -> defaultManifest(name)).increment(); manifest = CodecUtil.readOrCompute(PackManifest.CODEC, packPath.resolve(MANIFEST_FILE), () -> defaultManifest(name)).increment();
itemTextures = CodecUtil.readOrCompute(BedrockTextureAtlas.ITEM_ATLAS_CODEC, packPath.resolve(ITEM_ATLAS_FILE), itemTextures = CodecUtil.readOrCompute(BedrockTextureAtlas.ITEM_ATLAS_CODEC, packPath.resolve(ITEM_ATLAS_FILE),
() -> BedrockTextureAtlas.itemAtlas(name, BedrockTextures.builder())).textures().toBuilder(); () -> BedrockTextureAtlas.itemAtlas(name, BedrockTextures.builder())).textures().toBuilder();
reporter = new ProblemReporter.Collector(() -> "Bedrock pack " + name);
} }
public String name() { public String name() {
return name; return name;
} }
public void map(ItemStack stack) { public boolean map(ItemStack stack) {
mappings.map(stack, mapping -> { if (stack.isEmpty()) {
return false;
}
AtomicBoolean problems = new AtomicBoolean();
ProblemReporter mapReporter = new ProblemReporter() {
@Override
public @NotNull ProblemReporter forChild(PathElement child) {
return reporter.forChild(child);
}
@Override
public void report(Problem problem) {
problems.set(true);
reporter.report(problem);
}
};
mappings.map(stack, mapReporter, mapping -> {
// TODO a proper way to get texture from item model // TODO a proper way to get texture from item model
itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath()); itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath());
ResourceLocation texture = mapping.bedrockIdentifier(); ResourceLocation texture = mapping.bedrockIdentifier();
@@ -64,14 +93,27 @@ public class BedrockPack {
texturesToExport.add(texture); texturesToExport.add(texture);
AttachableMapper.mapItem(stack, mapping.bedrockIdentifier(), texturesToExport::add).ifPresent(attachables::add); AttachableMapper.mapItem(stack, mapping.bedrockIdentifier(), texturesToExport::add).ifPresent(attachables::add);
}); });
return problems.get();
} }
public void save() throws IOException { public boolean save() {
boolean success = true;
try {
CodecUtil.trySaveJson(GeyserMappings.CODEC, mappings, exportPath.resolve(MAPPINGS_FILE)); CodecUtil.trySaveJson(GeyserMappings.CODEC, mappings, exportPath.resolve(MAPPINGS_FILE));
CodecUtil.trySaveJson(PackManifest.CODEC, manifest, packPath.resolve(MANIFEST_FILE)); CodecUtil.trySaveJson(PackManifest.CODEC, manifest, packPath.resolve(MANIFEST_FILE));
CodecUtil.trySaveJson(BedrockTextureAtlas.CODEC, BedrockTextureAtlas.itemAtlas(name, itemTextures), packPath.resolve(ITEM_ATLAS_FILE)); CodecUtil.trySaveJson(BedrockTextureAtlas.CODEC, BedrockTextureAtlas.itemAtlas(name, itemTextures), packPath.resolve(ITEM_ATLAS_FILE));
} catch (IOException exception) {
reporter.forChild(() -> "saving Geyser mappings, pack manifest, and texture atlas ").report(() -> "failed to save to pack: " + exception);
success = false;
}
for (BedrockAttachable attachable : attachables) { for (BedrockAttachable attachable : attachables) {
try {
attachable.save(packPath.resolve(ATTACHABLES_DIRECTORY)); attachable.save(packPath.resolve(ATTACHABLES_DIRECTORY));
} catch (IOException exception) {
reporter.forChild(() -> "attachable for bedrock item " + attachable.info().identifier() + " ").report(() -> "failed to save to pack: " + exception);
success = false;
}
} }
for (ResourceLocation texture : texturesToExport) { for (ResourceLocation texture : texturesToExport) {
@@ -82,10 +124,19 @@ public class BedrockPack {
try (OutputStream outputTexture = new FileOutputStream(texturePath.toFile())) { try (OutputStream outputTexture = new FileOutputStream(texturePath.toFile())) {
IOUtils.copy(inputTexture, outputTexture); IOUtils.copy(inputTexture, outputTexture);
} }
} catch (FileNotFoundException exception) { } catch (IOException exception) {
// TODO ResourceLocation finalTexture = texture;
reporter.forChild(() -> "texture " + finalTexture + " ").report(() -> "failed to save to pack: " + exception);
success = false;
} }
} }
try {
Files.writeString(exportPath.resolve(REPORT_FILE), reporter.getTreeReport());
} catch (IOException exception) {
// TODO log
}
return success;
} }
private static Path createPackDirectory(String name) throws IOException { private static Path createPackDirectory(String name) throws IOException {