diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java index 8868887..4fa21f6 100644 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java +++ b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java @@ -118,6 +118,10 @@ public final class GltfClientSetup { LOGGER.info( "[GltfPipeline] Data-driven item reload listener registered" ); + + // GLB structural validation (runs after item definitions are loaded) + event.registerReloadListener(new com.tiedup.remake.client.gltf.diagnostic.GlbValidationReloadListener()); + LOGGER.info("[GltfPipeline] GLB validation reload listener registered"); } } diff --git a/src/main/java/com/tiedup/remake/client/gltf/diagnostic/GlbValidationReloadListener.java b/src/main/java/com/tiedup/remake/client/gltf/diagnostic/GlbValidationReloadListener.java new file mode 100644 index 0000000..bf724b4 --- /dev/null +++ b/src/main/java/com/tiedup/remake/client/gltf/diagnostic/GlbValidationReloadListener.java @@ -0,0 +1,138 @@ +package com.tiedup.remake.client.gltf.diagnostic; + +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Reload listener that validates all data-driven item GLB models on resource + * reload (F3+T or startup). + * + *

Must be registered AFTER {@link com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemReloadListener} + * so that the item registry is populated before validation runs.

+ * + *

On each reload: + *

    + *
  1. Clears the {@link GlbDiagnosticRegistry}
  2. + *
  3. Iterates all {@link DataDrivenItemDefinition}s
  4. + *
  5. For each definition with a model location, attempts to open the GLB + * resource and runs {@link GlbValidator#validate} on it
  6. + *
  7. Records results into the diagnostic registry and logs a summary
  8. + *
+ */ +@OnlyIn(Dist.CLIENT) +public class GlbValidationReloadListener + extends SimplePreparableReloadListener +{ + + private static final Logger LOGGER = LogManager.getLogger("GltfValidation"); + + @Override + protected Void prepare( + ResourceManager resourceManager, + ProfilerFiller profiler + ) { + return null; + } + + @Override + protected void apply( + Void nothing, + ResourceManager resourceManager, + ProfilerFiller profiler + ) { + GlbDiagnosticRegistry.clear(); + + Collection definitions = + DataDrivenItemRegistry.getAll(); + + if (definitions.isEmpty()) { + LOGGER.warn( + "[GltfValidation] No data-driven item definitions found — skipping GLB validation" + ); + return; + } + + int passed = 0; + int withWarnings = 0; + int withErrors = 0; + + for (DataDrivenItemDefinition def : definitions) { + ResourceLocation modelLoc = def.modelLocation(); + if (modelLoc == null) { + continue; + } + + Optional resourceOpt = + resourceManager.getResource(modelLoc); + + if (resourceOpt.isEmpty()) { + // GLB file not found in any resource pack + GlbValidationResult missingResult = GlbValidationResult.of( + modelLoc, + List.of(new GlbDiagnostic( + modelLoc, + def.id(), + GlbDiagnostic.Severity.ERROR, + "MISSING_GLB", + "GLB file not found: " + modelLoc + + " (referenced by item " + def.id() + ")" + )) + ); + GlbDiagnosticRegistry.addResult(missingResult); + withErrors++; + continue; + } + + try (InputStream stream = resourceOpt.get().open()) { + GlbValidationResult result = + GlbValidator.validate(stream, modelLoc); + GlbDiagnosticRegistry.addResult(result); + + if (!result.passed()) { + withErrors++; + } else if (hasWarnings(result)) { + withWarnings++; + } else { + passed++; + } + } catch (Exception e) { + GlbValidationResult errorResult = GlbValidationResult.of( + modelLoc, + List.of(new GlbDiagnostic( + modelLoc, + def.id(), + GlbDiagnostic.Severity.ERROR, + "GLB_READ_ERROR", + "Failed to read GLB file: " + e.getMessage() + )) + ); + GlbDiagnosticRegistry.addResult(errorResult); + withErrors++; + } + } + + int total = passed + withWarnings + withErrors; + LOGGER.info( + "[GltfValidation] Validated {} GLBs: {} passed, {} with warnings, {} with errors", + total, passed, withWarnings, withErrors + ); + } + + private static boolean hasWarnings(GlbValidationResult result) { + return result.diagnostics().stream() + .anyMatch(d -> d.severity() == GlbDiagnostic.Severity.WARNING); + } +}