1
0
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:
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 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;
}
}
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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);
});
}

View File

@@ -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 {