mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Work on problem reporting
This commit is contained in:
@@ -27,6 +27,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
|
||||
private final List<String> pendingPackCommands = new ArrayList<>();
|
||||
private boolean waitingOnItem = false;
|
||||
private boolean waitingOnClear = false;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
@@ -50,7 +51,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
.then(ClientCommandManager.literal("map")
|
||||
.executes(context -> {
|
||||
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"));
|
||||
return 0;
|
||||
})
|
||||
@@ -59,7 +60,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
.executes(context -> {
|
||||
int mapped = 0;
|
||||
for (ItemStack stack : context.getSource().getPlayer().getInventory()) {
|
||||
if (PackManager.getInstance().map(stack, false)) {
|
||||
if (PackManager.getInstance().map(stack)) {
|
||||
mapped++;
|
||||
}
|
||||
}
|
||||
@@ -69,10 +70,8 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
)
|
||||
.then(ClientCommandManager.literal("finish")
|
||||
.executes(context -> {
|
||||
try {
|
||||
PackManager.getInstance().finish();
|
||||
} catch (IOException exception) {
|
||||
throw new SimpleCommandExceptionType(Component.literal(exception.getMessage())).create();
|
||||
if (!PackManager.getInstance().finish()) {
|
||||
throw new SimpleCommandExceptionType(Component.literal("Errors occurred whilst trying to write the pack to disk!")).create();
|
||||
}
|
||||
context.getSource().sendFeedback(Component.literal("Wrote pack to disk"));
|
||||
return 0;
|
||||
@@ -80,8 +79,10 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
)
|
||||
.then(ClientCommandManager.literal("auto")
|
||||
.then(ClientCommandManager.argument("namespace", StringArgumentType.word())
|
||||
.then(ClientCommandManager.argument("path", StringArgumentType.string())
|
||||
.executes(context -> {
|
||||
String namespace = StringArgumentType.getString(context, "namespace");
|
||||
String path = StringArgumentType.getString(context, "path");
|
||||
|
||||
// Duplicated code, this is just to try this out
|
||||
try {
|
||||
@@ -93,7 +94,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
|
||||
// hack
|
||||
CommandContext<?> suggestionsContext = new CommandContext<>(null,
|
||||
"loot give @s loot " + namespace + ":",
|
||||
"loot give @s loot " + namespace + ":" + path,
|
||||
null, null, null, null, null, null, null, false);
|
||||
context.getSource().getClient().getConnection().getSuggestionsProvider()
|
||||
.customSuggestion(suggestionsContext)
|
||||
@@ -102,12 +103,15 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
ClientTickEvents.START_CLIENT_TICK.register(client -> {
|
||||
if (!pendingPackCommands.isEmpty() || waitingOnItem) {
|
||||
if (!waitingOnItem) {
|
||||
if (!pendingPackCommands.isEmpty() || waitingOnItem || waitingOnClear) {
|
||||
if (waitingOnClear && client.player.getInventory().isEmpty()) {
|
||||
waitingOnClear = false;
|
||||
} else if (!waitingOnItem) {
|
||||
String command = pendingPackCommands.removeFirst();
|
||||
client.getConnection().send(new ServerboundChatCommandPacket("loot give @s loot " + command));
|
||||
waitingOnItem = true;
|
||||
@@ -116,7 +120,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
int mapped = 0;
|
||||
for (ItemStack stack : client.player.getInventory()) {
|
||||
try {
|
||||
if (PackManager.getInstance().map(stack, false)) {
|
||||
if (PackManager.getInstance().map(stack)) {
|
||||
mapped++;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
@@ -130,11 +134,16 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
waitingOnItem = false;
|
||||
if (pendingPackCommands.isEmpty()) {
|
||||
try {
|
||||
PackManager.getInstance().finish();
|
||||
} catch (IOException | CommandSyntaxException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
if (!PackManager.getInstance().finish()) {
|
||||
client.player.displayClientMessage(Component.literal("Errors occurred whilst trying to write the pack to disk!"), false);
|
||||
return;
|
||||
}
|
||||
} catch (CommandSyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
client.player.displayClientMessage(Component.literal("Wrote pack to disk"), false);
|
||||
} else {
|
||||
waitingOnClear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,25 +24,17 @@ public final class PackManager {
|
||||
currentPack = new BedrockPack(name);
|
||||
}
|
||||
|
||||
public boolean map(ItemStack stack, boolean throwOnModelMissing) throws CommandSyntaxException {
|
||||
public boolean map(ItemStack stack) throws CommandSyntaxException {
|
||||
ensurePackIsCreated();
|
||||
|
||||
try {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return currentPack.map(stack);
|
||||
}
|
||||
|
||||
public void finish() throws CommandSyntaxException, IOException {
|
||||
public boolean finish() throws CommandSyntaxException {
|
||||
ensurePackIsCreated();
|
||||
currentPack.save();
|
||||
boolean success = currentPack.save();
|
||||
currentPack = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
private void ensurePackIsCreated() throws CommandSyntaxException {
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.item.CrossbowItem;
|
||||
import net.minecraft.world.item.equipment.trim.TrimMaterial;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -37,9 +38,10 @@ import java.util.stream.Stream;
|
||||
|
||||
public class GeyserItemMapper {
|
||||
|
||||
public static Stream<GeyserMapping> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch) {
|
||||
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch);
|
||||
public static Stream<GeyserMapping> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch,
|
||||
ProblemReporter reporter) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -53,14 +55,15 @@ public class GeyserItemMapper {
|
||||
return Stream.of(context.create(itemModel));
|
||||
}
|
||||
case ConditionalItemModel conditional -> {
|
||||
return mapConditionalModel(conditional, context);
|
||||
return mapConditionalModel(conditional, context.child("condition " + conditional + " "));
|
||||
}
|
||||
case SelectItemModel<?> select -> {
|
||||
return mapSelectModel(select, context);
|
||||
return mapSelectModel(select, context.child("select " + select + " "));
|
||||
}
|
||||
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) {
|
||||
@@ -71,15 +74,19 @@ public class GeyserItemMapper {
|
||||
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 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 onFalse = ((ConditionalItemModelAccessor) model).getOnFalse();
|
||||
|
||||
return Stream.concat(
|
||||
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true))),
|
||||
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false)))
|
||||
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true), "condition on true ")),
|
||||
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);
|
||||
// Why, Mojang?
|
||||
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
|
||||
Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).geyser_mappings_generator$getCases();
|
||||
return Stream.concat(
|
||||
cases.entrySet().stream()
|
||||
.flatMap(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey()))))),
|
||||
mapItem(cases.defaultReturnValue(), context)
|
||||
.flatMap(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey())), "select case " + caze.getKey() + " "))),
|
||||
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) {
|
||||
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch);
|
||||
public MappingContext with(GeyserPredicate predicate, String childName) {
|
||||
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) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.geysermc.packgenerator.CodecUtil;
|
||||
@@ -48,20 +49,25 @@ public class GeyserMappings {
|
||||
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);
|
||||
//noinspection OptionalAssignedToNull - annoying Mojang
|
||||
if (patchedModel == null || patchedModel.isEmpty()) {
|
||||
throw new IllegalArgumentException("Item stack does not have a custom model");
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceLocation model = patchedModel.get();
|
||||
String displayName = stack.getHoverName().getString();
|
||||
int protectionValue = 0; // TODO check the attributes
|
||||
|
||||
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch())
|
||||
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter)
|
||||
.forEach(mapping -> {
|
||||
try {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.geysermc.packgenerator.pack;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
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.geyser.GeyserMappings;
|
||||
import org.geysermc.packgenerator.pack.attachable.BedrockAttachable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class BedrockPack {
|
||||
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 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 Path exportPath;
|
||||
private final Path packPath;
|
||||
@@ -39,22 +45,45 @@ public class BedrockPack {
|
||||
private final List<BedrockAttachable> attachables = new ArrayList<>();
|
||||
private final List<ResourceLocation> texturesToExport = new ArrayList<>();
|
||||
|
||||
private final ProblemReporter.Collector reporter;
|
||||
|
||||
public BedrockPack(String name) throws IOException {
|
||||
this.name = name;
|
||||
|
||||
exportPath = createPackDirectory(name);
|
||||
packPath = exportPath.resolve(PACK_DIRECTORY);
|
||||
mappings = CodecUtil.readOrCompute(GeyserMappings.CODEC, exportPath.resolve(MAPPINGS_FILE), GeyserMappings::new);
|
||||
manifest = CodecUtil.readOrCompute(PackManifest.CODEC, packPath.resolve(MANIFEST_FILE), () -> defaultManifest(name)).increment();
|
||||
itemTextures = CodecUtil.readOrCompute(BedrockTextureAtlas.ITEM_ATLAS_CODEC, packPath.resolve(ITEM_ATLAS_FILE),
|
||||
() -> BedrockTextureAtlas.itemAtlas(name, BedrockTextures.builder())).textures().toBuilder();
|
||||
|
||||
reporter = new ProblemReporter.Collector(() -> "Bedrock pack " + name);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void map(ItemStack stack) {
|
||||
mappings.map(stack, mapping -> {
|
||||
public boolean map(ItemStack stack) {
|
||||
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
|
||||
itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath());
|
||||
ResourceLocation texture = mapping.bedrockIdentifier();
|
||||
@@ -64,14 +93,27 @@ public class BedrockPack {
|
||||
texturesToExport.add(texture);
|
||||
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(PackManifest.CODEC, manifest, packPath.resolve(MANIFEST_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) {
|
||||
try {
|
||||
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) {
|
||||
@@ -82,10 +124,19 @@ public class BedrockPack {
|
||||
try (OutputStream outputTexture = new FileOutputStream(texturePath.toFile())) {
|
||||
IOUtils.copy(inputTexture, outputTexture);
|
||||
}
|
||||
} catch (FileNotFoundException exception) {
|
||||
// TODO
|
||||
} catch (IOException exception) {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user