From 35852bdcdca240c942408322d1fb3a12c18546fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 17:54:57 +0200 Subject: [PATCH 01/13] First batch of changes --- .../stationapi/api/effect/EntityEffect.java | 57 +++++++++++-------- .../api/effect/EntityEffectType.java | 24 ++++++++ .../impl/effect/EffectsRenderer.java | 8 +-- .../api/util/math/StationBlockPos.java | 46 ++++++++++++--- .../{TilePosMixin.java => BlockPosMixin.java} | 8 +-- .../resources/station-maths-v0.mixins.json | 2 +- .../UnbakedModelLoadingFinishedEvent.java | 29 ++++++++++ .../render/model/BakedModelManager.java | 2 +- .../api/client/render/model/BakedQuad.java | 4 ++ .../api/client/render/model/ModelLoader.java | 9 ++- .../render/model/WeightedBakedModel.java | 22 ++++++- .../render/model/json/JsonUnbakedModel.java | 13 ++++- 12 files changed, 181 insertions(+), 43 deletions(-) rename station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/{TilePosMixin.java => BlockPosMixin.java} (92%) create mode 100644 station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java diff --git a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffect.java b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffect.java index d2e2b4381..d87815e8a 100644 --- a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffect.java +++ b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffect.java @@ -1,7 +1,5 @@ package net.modificationstation.stationapi.api.effect; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.minecraft.client.resource.language.I18n; import net.minecraft.entity.Entity; import net.minecraft.nbt.NbtCompound; @@ -10,16 +8,15 @@ public abstract class EntityEffect> { public static final int INFINITY_TICKS = -1; protected Entity entity; private int ticks; - + private final String nameTranslationKey; private final String descriptionTranslationKey; - + protected EntityEffect(Entity entity, int ticks) { this.entity = entity; this.ticks = ticks; - var effectId = getType().registryEntry.registryKey().getValue(); - nameTranslationKey = "gui.stationapi.effect." + effectId.namespace + "." + effectId.path + ".name"; - descriptionTranslationKey = nameTranslationKey.substring(0, nameTranslationKey.length() - 4) + "desc"; + this.nameTranslationKey = getType().getTranslationKey(); + this.descriptionTranslationKey = getType().getDescriptionTranslationKey(); } /** @@ -29,58 +26,72 @@ protected EntityEffect(Entity entity, int ticks) { * or synchronized from the server later. */ public abstract void onAdded(boolean appliedNow); - + /** * This method is called on each entity tick. */ public abstract void onTick(); - + /** * This method is called immediately when the effect is removed. */ public abstract void onRemoved(); - + /** * Allows to write any custom data to the tag storage. + * * @param tag effect data root tag */ protected abstract void writeNbt(NbtCompound tag); - + /** * Allows to read any custom data from the tag storage. + * * @param tag effect data root tag */ protected abstract void readNbt(NbtCompound tag); public abstract EntityEffectType getType(); - + /** * Get remaining effect ticks. */ public final int getTicks() { return ticks; } - + /** * Check if effect is infinite. */ public final boolean isInfinite() { return ticks == INFINITY_TICKS; } - + /** - * Get translated effect name, client side only. + * Get the translation key for the name of the effect. */ - @Environment(EnvType.CLIENT) - public final String getName() { + public final String getTranslationKey() { + return this.nameTranslationKey; + } + + /** + * Get the translation key for the description of the effect. + */ + public final String getDescriptionTranslationKey() { + return this.descriptionTranslationKey; + } + + /** + * Get translated effect name. + */ + public final String getTranslatedName() { return I18n.getTranslation(nameTranslationKey, nameTranslationKey); } - + /** - * Get translated effect description, client side only. + * Get translated effect description. */ - @Environment(EnvType.CLIENT) - public final String getDescription() { + public final String getTranslatedDescription() { return I18n.getTranslation(descriptionTranslationKey, descriptionTranslationKey); } @@ -93,12 +104,12 @@ public final void tick() { } } } - + public final void write(NbtCompound nbt) { nbt.putInt("ticks", ticks); writeNbt(nbt); } - + public final void read(NbtCompound nbt) { ticks = nbt.getInt("ticks"); readNbt(nbt); diff --git a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffectType.java b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffectType.java index c14e43662..cca825174 100644 --- a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffectType.java +++ b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/api/effect/EntityEffectType.java @@ -1,6 +1,7 @@ package net.modificationstation.stationapi.api.effect; import net.modificationstation.stationapi.api.registry.RegistryEntry; +import net.modificationstation.stationapi.api.util.Identifier; /** * Static reference of an effect. Since {@link EntityEffect} is initialized per-entity, a separate class is required @@ -24,6 +25,29 @@ private EntityEffectType(Builder builder) { factory = builder.factory; } + /** + * Returns the identifier of the effect + */ + public Identifier getIdentifier() { + return registryEntry.registryKey().getValue(); + } + + /** + * Returns the translation key of the effect's name. + */ + public String getTranslationKey() { + Identifier identifier = registryEntry.registryKey().getValue(); + return "gui.stationapi.effect." + identifier.namespace + "." + identifier.path + ".name"; + } + + /** + * Returns the translation key of the effect's description. + */ + public String getDescriptionTranslationKey() { + Identifier identifier = registryEntry.registryKey().getValue(); + return "gui.stationapi.effect." + identifier.namespace + "." + identifier.path + ".desc"; + } + /** * @param factory the effect instance's factory which takes an {@link net.minecraft.entity.Entity} argument * of the entity the effect's been applied to, and an int argument defining the duration diff --git a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/impl/effect/EffectsRenderer.java b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/impl/effect/EffectsRenderer.java index dd27dc80f..beee1e492 100644 --- a/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/impl/effect/EffectsRenderer.java +++ b/station-effects-api-v0/src/main/java/net/modificationstation/stationapi/impl/effect/EffectsRenderer.java @@ -40,8 +40,8 @@ public void renderEffects(Minecraft minecraft, float delta, boolean extended) { int py = 2; for (EntityEffect effect : renderEffects) { if (extended) { - String name = effect.getName(); - String desc = effect.getDescription(); + String name = effect.getTranslatedName(); + String desc = effect.getTranslatedDescription(); int width = Math.max( textRenderer.getWidth(name), textRenderer.getWidth(desc) @@ -127,8 +127,8 @@ private void renderEffectBack(Minecraft minecraft, int y, int width) { private int getEffectWidth(EntityEffect effect) { if (effect.isInfinite()) return 26; - String name = effect.getName(); - String desc = effect.getDescription(); + String name = effect.getTranslatedName(); + String desc = effect.getTranslatedDescription(); return Math.max( textRenderer.getWidth(name), textRenderer.getWidth(desc) diff --git a/station-maths-v0/src/main/java/net/modificationstation/stationapi/api/util/math/StationBlockPos.java b/station-maths-v0/src/main/java/net/modificationstation/stationapi/api/util/math/StationBlockPos.java index 3c4aff045..2c463d1fc 100644 --- a/station-maths-v0/src/main/java/net/modificationstation/stationapi/api/util/math/StationBlockPos.java +++ b/station-maths-v0/src/main/java/net/modificationstation/stationapi/api/util/math/StationBlockPos.java @@ -41,14 +41,14 @@ static BlockPos create(double x, double y, double z) { return new BlockPos(floor(x), floor(y), floor(z)); } - static BlockPos create(Vec3d pos) { - return create(pos.x, pos.y, pos.z); - } - static BlockPos create(Position pos) { return create(pos.getX(), pos.getY(), pos.getZ()); } + static BlockPos create(Vec3d pos) { + return create(pos.x, pos.y, pos.z); + } + static BlockPos create(Vec3i pos) { return new BlockPos(pos.getX(), pos.getY(), pos.getZ()); } @@ -336,23 +336,55 @@ default long asLong() { return asLong(getX(), getY(), getZ()); } - default BlockPos add(double d, double e, double f) { + default BlockPos add(double x, double y, double z) { return Util.assertImpl(); } - default BlockPos add(int i, int j, int k) { + default BlockPos add(int x, int y, int z) { return Util.assertImpl(); } + default BlockPos add(BlockPos blockPos) { + return add(blockPos.x, blockPos.y, blockPos.z); + } + + default BlockPos add(Position position) { + return add(position.getX(), position.getY(), position.getZ()); + } + + default BlockPos add(Vec3d vec3d) { + return add(vec3d.x, vec3d.y, vec3d.z); + } + default BlockPos add(Vec3i vec3i) { return add(vec3i.getX(), vec3i.getY(), vec3i.getZ()); } + default BlockPos subtract(double x, double y, double z) { + return add(-x, -y, -z); + } + + default BlockPos subtract(int x, int y, int z) { + return add(-x, -y, -z); + } + + default BlockPos subtract(BlockPos blockPos) { + return add(-blockPos.x, -blockPos.y, -blockPos.z); + } + + default BlockPos subtract(Position position) { + return add(-position.getX(), -position.getY(), -position.getZ()); + } + + default BlockPos subtract(Vec3d vec3d) { + return add(-vec3d.x, -vec3d.y, -vec3d.z); + } + default BlockPos subtract(Vec3i vec3i) { return add(-vec3i.getX(), -vec3i.getY(), -vec3i.getZ()); } - default BlockPos multiply(int i) { + default BlockPos multiply(int multiplier) { return Util.assertImpl(); } diff --git a/station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/TilePosMixin.java b/station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/BlockPosMixin.java similarity index 92% rename from station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/TilePosMixin.java rename to station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/BlockPosMixin.java index 7aadd2cf5..b2675068c 100644 --- a/station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/TilePosMixin.java +++ b/station-maths-v0/src/main/java/net/modificationstation/stationapi/mixin/maths/BlockPosMixin.java @@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(BlockPos.class) -public class TilePosMixin implements StationBlockPos { +public class BlockPosMixin implements StationBlockPos { @Shadow @Final public int x; @Shadow @Final public int y; @@ -56,12 +56,12 @@ public BlockPos subtract(Vec3i vec3i) { } @Override - public BlockPos multiply(int i) { - return switch (i) { + public BlockPos multiply(int multiplier) { + return switch (multiplier) { case 1 -> //noinspection DataFlowIssue (BlockPos) (Object) this; case 0 -> ORIGIN; - default -> new BlockPos(getX() * i, getY() * i, getZ() * i); + default -> new BlockPos(getX() * multiplier, getY() * multiplier, getZ() * multiplier); }; } diff --git a/station-maths-v0/src/main/resources/station-maths-v0.mixins.json b/station-maths-v0/src/main/resources/station-maths-v0.mixins.json index 153251cdd..dd396faed 100644 --- a/station-maths-v0/src/main/resources/station-maths-v0.mixins.json +++ b/station-maths-v0/src/main/resources/station-maths-v0.mixins.json @@ -5,7 +5,7 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "TilePosAccessor", - "TilePosMixin" + "BlockPosMixin" ], "injectors": { "defaultRequire": 1 diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java new file mode 100644 index 000000000..fada8e564 --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java @@ -0,0 +1,29 @@ +package net.modificationstation.stationapi.api.client.event.render.model; + +import net.mine_diver.unsafeevents.Event; +import net.mine_diver.unsafeevents.event.EventPhases; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.client.color.block.BlockColors; +import net.modificationstation.stationapi.api.client.render.model.ModelLoader; +import net.modificationstation.stationapi.api.client.render.model.UnbakedModel; +import net.modificationstation.stationapi.api.util.Identifier; + +import java.util.List; +import java.util.Map; + +@EventPhases(StationAPI.INTERNAL_PHASE) +public class UnbakedModelLoadingFinishedEvent extends Event { + public ModelLoader modelLoader; + public Map unbakedModels; + public Map modelsToBake; + public Map> blockStates; + public BlockColors blockColors; + + public UnbakedModelLoadingFinishedEvent(ModelLoader modelLoader, Map unbakedModels, Map modelsToBake, Map> blockStates, BlockColors blockColors) { + this.modelLoader = modelLoader; + this.unbakedModels = unbakedModels; + this.modelsToBake = modelsToBake; + this.blockStates = blockStates; + this.blockColors = blockColors; + } +} diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java index aea0398ca..d6348cacc 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java @@ -187,7 +187,7 @@ private void upload(BakingResult bakingResult, Profiler profiler) { profiler.endTick(); } - record BakingResult(ModelLoader modelLoader, BakedModel missingModel, Map modelCache, Map atlasPreparations, CompletableFuture readyForUpload) {} + public record BakingResult(ModelLoader modelLoader, BakedModel missingModel, Map modelCache, Map atlasPreparations, CompletableFuture readyForUpload) {} @Override public Identifier getId() { diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedQuad.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedQuad.java index 74818cef4..2dd45d222 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedQuad.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedQuad.java @@ -43,6 +43,10 @@ public Direction getFace() { return this.face; } + public Sprite getSprite() { + return sprite; + } + public boolean hasShade() { return this.shade; } diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java index 23ab77d35..3833f1fa9 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java @@ -17,6 +17,7 @@ import net.modificationstation.stationapi.api.block.BlockStateHolder; import net.modificationstation.stationapi.api.client.color.block.BlockColors; import net.modificationstation.stationapi.api.client.event.render.model.LoadUnbakedModelEvent; +import net.modificationstation.stationapi.api.client.event.render.model.UnbakedModelLoadingFinishedEvent; import net.modificationstation.stationapi.api.client.event.render.model.PreLoadUnbakedModelEvent; import net.modificationstation.stationapi.api.client.render.block.BlockModels; import net.modificationstation.stationapi.api.client.render.model.json.JsonUnbakedModel; @@ -55,7 +56,6 @@ @Environment(EnvType.CLIENT) public class ModelLoader { - public static final List BLOCK_DESTRUCTION_STAGES = IntStream.range(0, 10).mapToObj(stage -> Identifier.of("block/destroy_stage_" + stage)).collect(Collectors.toList()); public static final List BLOCK_DESTRUCTION_STAGE_TEXTURES = BLOCK_DESTRUCTION_STAGES.stream().map(id -> Identifier.of(NAMESPACE + "/textures/" + id.path + ".png")).collect(Collectors.toList()); public static final ModelIdentifier MISSING_ID; @@ -87,6 +87,7 @@ public ModelLoader(BlockColors blockColors, Profiler profiler, Map stateManager.getStates().forEach(state -> this.addModel(BlockModels.getModelId(id, state).asIdentifier()))); + profiler.swap("blocks"); for (Block block : BlockRegistry.INSTANCE) block.getStateManager().getStates().forEach(state -> this.addModel(BlockModels.getModelId(state).asIdentifier())); + profiler.swap("items"); for (Identifier identifier : ItemRegistry.INSTANCE.getIds()) this.addModel(ModelIdentifier.of(identifier, "inventory").asIdentifier()); + profiler.swap("special"); this.modelsToBake.values().forEach(model -> model.setParents(this::getOrLoadModel)); + profiler.pop(); + StationAPI.EVENT_BUS.post(new UnbakedModelLoadingFinishedEvent(this, this.unbakedModels, this.modelsToBake, this.blockStates, this.blockColors)); } public void bake(BiFunction spriteLoader) { diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/WeightedBakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/WeightedBakedModel.java index a9059606f..c77c57a33 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/WeightedBakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/WeightedBakedModel.java @@ -32,6 +32,10 @@ public WeightedBakedModel(List models) { public ImmutableList getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) { return Objects.requireNonNull(WeightedPicker.getAt(this.models, Math.abs((int) random.nextLong()) % this.totalWeight)).model.getQuads(state, face, random); } + + public Entry pickModel(Random random) { + return WeightedPicker.getAt(this.models, Math.abs((int) random.nextLong()) % this.totalWeight); + } public boolean useAmbientOcclusion() { return this.defaultModel.useAmbientOcclusion(); @@ -61,14 +65,30 @@ public ModelOverrideList getOverrides() { return this.defaultModel.getOverrides(); } + public int getTotalWeight() { + return totalWeight; + } + + public List getModels() { + return models; + } + + public BakedModel getDefaultModel() { + return defaultModel; + } + @Environment(EnvType.CLIENT) - static class Entry extends WeightedPicker.Entry { + public static class Entry extends WeightedPicker.Entry { protected final BakedModel model; public Entry(BakedModel model, int weight) { super(weight); this.model = model; } + + public BakedModel getModel() { + return model; + } } @Environment(EnvType.CLIENT) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index ebe10b9d7..16669ca54 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -29,7 +29,6 @@ import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; public final class JsonUnbakedModel implements UnbakedModel { - public static final Identifier BUILTIN_GENERATED = Identifier.of("builtin/generated"); private static final BakedQuadFactory QUAD_FACTORY = new BakedQuadFactory(); @@ -94,6 +93,18 @@ private ModelOverrideList compileOverrides(Baker baker, JsonUnbakedModel parent) return this.overrides.isEmpty() ? ModelOverrideList.EMPTY : new ModelOverrideList(baker, parent, baker::getOrLoadModel, this.overrides); } + public @Nullable JsonUnbakedModel getParent() { + return parent; + } + + public @Nullable Identifier getParentId() { + return parentId; + } + + public Map> getTextureMap() { + return textureMap; + } + public Collection getModelDependencies() { ReferenceSet set = new ReferenceOpenHashSet<>(); for (ModelOverride modelOverride : this.overrides) set.add(modelOverride.getModelId()); From 4c58d72626d529c3a671a66113737a7bb9929fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 18:25:10 +0200 Subject: [PATCH 02/13] Fix a Dany L Moment --- .../stationapi/mixin/StationRecipesMixinPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/StationRecipesMixinPlugin.java b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/StationRecipesMixinPlugin.java index 481fa44e1..0611ec5c7 100644 --- a/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/StationRecipesMixinPlugin.java +++ b/station-recipes-v0/src/main/java/net/modificationstation/stationapi/mixin/StationRecipesMixinPlugin.java @@ -37,7 +37,7 @@ public List getMixins() { return null; } - if (FabricLoader.getInstance().isModLoaded("alwaysmoreitems")) { + if (!FabricLoader.getInstance().isModLoaded("alwaysmoreitems")) { return null; } From 869ac437549c05412194c2c6a332423d06d6be7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 18:50:15 +0200 Subject: [PATCH 03/13] BlockStateReloadEvent --- .../sltest/render/ModelLoadingListener.java | 27 ++++++ src/test/resources/fabric.mod.json | 3 +- .../render/model/BlockStateReloadEvent.java | 97 +++++++++++++++++++ .../render/model/BakedModelManager.java | 6 +- 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java create mode 100644 station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java diff --git a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java new file mode 100644 index 000000000..168b3cc26 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java @@ -0,0 +1,27 @@ +package net.modificationstation.sltest.render; + +import net.mine_diver.unsafeevents.listener.EventListener; +import net.modificationstation.sltest.SLTest; +import net.modificationstation.stationapi.api.client.event.render.model.BlockStateReloadEvent; +import net.modificationstation.stationapi.api.client.event.render.model.UnbakedModelLoadingFinishedEvent; +import net.modificationstation.stationapi.api.util.Identifier; + +public class ModelLoadingListener { + @EventListener + public void modelsLodaded(UnbakedModelLoadingFinishedEvent event) { + SLTest.LOGGER.info(event.unbakedModels.size() + " models loaded"); + } + + String blockstateJson = (""" + { + "variants": { + "": {"model": "sltest:block/test_block"} + } + }""" + ); + + @EventListener + public void injectBlockState(BlockStateReloadEvent event) { + event.addBlockstateJsonString(Identifier.of("minecraft:sponge"), blockstateJson); + } +} diff --git a/src/test/resources/fabric.mod.json b/src/test/resources/fabric.mod.json index f77d035c0..12056434e 100644 --- a/src/test/resources/fabric.mod.json +++ b/src/test/resources/fabric.mod.json @@ -41,7 +41,8 @@ "net.modificationstation.sltest.option.OptionListener", "net.modificationstation.sltest.keyboard.KeyboardListener", "net.modificationstation.sltest.texture.TextureListener", - "net.modificationstation.sltest.render.entity.EntityRendererListener" + "net.modificationstation.sltest.render.entity.EntityRendererListener", + "net.modificationstation.sltest.render.ModelLoadingListener" ], "main": [ "net.modificationstation.sltest.MainTest" diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java new file mode 100644 index 000000000..5ba22657d --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java @@ -0,0 +1,97 @@ +package net.modificationstation.stationapi.api.client.event.render.model; + +import com.mojang.datafixers.util.Pair; +import net.mine_diver.unsafeevents.Event; +import net.mine_diver.unsafeevents.event.EventPhases; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.client.render.model.ModelLoader; +import net.modificationstation.stationapi.api.resource.Resource; +import net.modificationstation.stationapi.api.resource.ResourceManager; +import net.modificationstation.stationapi.api.util.Identifier; +import net.modificationstation.stationapi.api.util.JsonHelper; + +import java.io.BufferedReader; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; + +/** + * An event which is fired when block states are being reloaded + */ +@EventPhases(StationAPI.INTERNAL_PHASE) +public class BlockStateReloadEvent extends Event { + public final ResourceManager resourceManager; + public final Executor executor; + public final List>>> states; + + public BlockStateReloadEvent(ResourceManager resourceManager, Executor executor, List>>> states) { + this.resourceManager = resourceManager; + this.executor = executor; + this.states = states; + } + + /** + * Adds a blockstate json to the list of blockstates to be loaded + * + * @param blockIdentifier The {@link Identifier} of the block + * @param jsons A string representation of the blockstate json to be deserialized + */ + public void addBlockstateJsonString(Identifier blockIdentifier, ArrayList jsons) { + Identifier id = Identifier.of(blockIdentifier.namespace + ":stationapi/blockstates/" + blockIdentifier.path + ".json"); + + states.add(CompletableFuture.supplyAsync(() -> { + List statesList = new ArrayList<>(jsons.size()); + for (String jsonString : jsons) + try { + statesList.add(new ModelLoader.SourceTrackedData("Default", JsonHelper.deserialize(jsonString))); + } catch (Exception exception) { + LOGGER.error("Failed to load blockstate {} from pack {}", blockIdentifier, "Default", exception); + } + return Pair.of(id, statesList); + }, executor)); + } + + /** + * Adds a blockstate json to the list of blockstates to be loaded + * + * @param blockIdentifier The {@link Identifier} of the block + * @param json Resource to be read and deserialized + */ + public void addBlockstateJsonString(Identifier blockIdentifier, String json) { + addBlockstateJsonString(blockIdentifier, new ArrayList<>(List.of(json))); + } + + /** + * Adds a resource to the list of blockstates to be loaded + * + * @param blockIdentifier The {@link Identifier} of the block + * @param resources Resources to be read and deserialized + */ + public void addBlockstateJsonResource(Identifier blockIdentifier, ArrayList resources) { + Identifier id = Identifier.of(blockIdentifier.namespace + ":stationapi/blockstates/" + blockIdentifier.path + ".json"); + + states.add(CompletableFuture.supplyAsync(() -> { + List list2 = new ArrayList<>(resources.size()); + for (Resource resource : resources) + try (BufferedReader reader = resource.getReader()) { + list2.add(new ModelLoader.SourceTrackedData("Default", JsonHelper.deserialize(reader))); + } catch (Exception exception) { + LOGGER.error("Failed to load blockstate {} from pack {}", blockIdentifier, "Default", exception); + } + return Pair.of(id, list2); + }, executor)); + } + + /** + * Adds a resource to the list of blockstates to be loaded + * + * @param blockIdentifier The {@link Identifier} of the block + * @param resource Resource to be read and deserialized + */ + public void addBlockStateJsonResource(Identifier blockIdentifier, Resource resource) { + addBlockstateJsonResource(blockIdentifier, new ArrayList<>(List.of(resource))); + } +} diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java index d6348cacc..a9abc82e4 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java @@ -12,8 +12,10 @@ import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.texture.TextureManager; +import net.modificationstation.stationapi.api.StationAPI; import net.modificationstation.stationapi.api.block.BlockState; import net.modificationstation.stationapi.api.client.color.block.BlockColors; +import net.modificationstation.stationapi.api.client.event.render.model.BlockStateReloadEvent; import net.modificationstation.stationapi.api.client.render.block.BlockModels; import net.modificationstation.stationapi.api.client.render.model.json.JsonUnbakedModel; import net.modificationstation.stationapi.api.client.texture.Sprite; @@ -129,7 +131,8 @@ private static CompletableFuture> reloadModels private static CompletableFuture>> reloadBlockStates(ResourceManager resourceManager, Executor executor) { return CompletableFuture.supplyAsync(() -> ModelLoader.BLOCK_STATES_FINDER.findAllResources(resourceManager), executor).thenCompose(blockStates2 -> { List>>> list = new ArrayList<>(blockStates2.size()); - for (Map.Entry> entry : blockStates2.entrySet()) + StationAPI.EVENT_BUS.post(new BlockStateReloadEvent(resourceManager, executor, list)); + for (Map.Entry> entry : blockStates2.entrySet()) { list.add(CompletableFuture.supplyAsync(() -> { List resources = entry.getValue(); List list2 = new ArrayList<>(resources.size()); @@ -141,6 +144,7 @@ private static CompletableFuture blockStates.stream().filter(Objects::nonNull).collect(Collectors.toUnmodifiableMap(Pair::getFirst, Pair::getSecond))); }); } From 0c23b00ab27cd006ccf94faae912ec802b85c0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 18:50:24 +0200 Subject: [PATCH 04/13] Cant run without this --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index bfc3c0ad7..0605addce 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -147,6 +147,7 @@ allprojects { // this fixes some edge cases with special characters not displaying correctly // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html tasks.withType { + options.release = 17 options.encoding = "UTF-8" } From 70633749c86c48892e6c5f9a56b0a36df8013811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 20:01:14 +0200 Subject: [PATCH 05/13] oops --- .../modificationstation/sltest/render/ModelLoadingListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java index 168b3cc26..82d808214 100644 --- a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java +++ b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java @@ -8,7 +8,7 @@ public class ModelLoadingListener { @EventListener - public void modelsLodaded(UnbakedModelLoadingFinishedEvent event) { + public void modelsLoaded(UnbakedModelLoadingFinishedEvent event) { SLTest.LOGGER.info(event.unbakedModels.size() + " models loaded"); } From b4705e9f7173d024520c3433474625a5af31b460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 21:03:47 +0200 Subject: [PATCH 06/13] Brought back from the dead --- .../api/client/render/model/UnbakedModel.java | 4 ++ .../render/model/json/JsonUnbakedModel.java | 56 +++++++++++++++++++ .../model/json/MultipartUnbakedModel.java | 6 ++ .../model/json/WeightedUnbakedModel.java | 7 +++ 4 files changed, 73 insertions(+) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java index 768599d93..7bf06a0cb 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java @@ -1,5 +1,6 @@ package net.modificationstation.stationapi.api.client.render.model; +import com.mojang.datafixers.util.Pair; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.modificationstation.stationapi.api.client.texture.Sprite; @@ -8,6 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.Set; import java.util.function.Function; @Environment(EnvType.CLIENT) @@ -16,6 +18,8 @@ public interface UnbakedModel { void setParents(Function parents); + Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences); + @Nullable BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId); } diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index 16669ca54..1511ba4ca 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -3,8 +3,10 @@ import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.gson.*; import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import it.unimi.dsi.fastutil.objects.ReferenceSet; import net.fabricmc.api.EnvType; @@ -141,6 +143,60 @@ public void setParents(Function modelLoader) { }); } + @Override + public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { + Set set = Sets.newLinkedHashSet(); + + for(JsonUnbakedModel JsonModel = this; JsonModel.parentId != null && JsonModel.parent == null; JsonModel = JsonModel.parent) { + set.add(JsonModel); + UnbakedModel unbakedModel = unbakedModelGetter.apply(JsonModel.parentId); + if (unbakedModel == null) { + LOGGER.warn("No parent '{}' while loading model '{}'", this.parentId, JsonModel); + } + + if (set.contains(unbakedModel)) { + LOGGER.warn("Found 'parent' loop while loading model '{}' in chain: {} -> {}", JsonModel, set.stream().map(Object::toString).collect(Collectors.joining(" -> ")), this.parentId); + unbakedModel = null; + } + + if (unbakedModel == null) { + JsonModel.parentId = ModelLoader.MISSING_ID.asIdentifier(); + unbakedModel = unbakedModelGetter.apply(JsonModel.parentId); + } + + if (!(unbakedModel instanceof JsonUnbakedModel jsonModel)) { + throw new IllegalStateException("BlockModel parent has to be a block model."); + } + + JsonModel.parent = jsonModel; + } + + Set set2 = Sets.newHashSet(this.resolveSprite("particle")); + + for (ModelElement modelElement : this.getElements()) { + SpriteIdentifier spriteIdentifier; + for (ModelElementFace modelElementFace : modelElement.faces.values()) { + spriteIdentifier = this.resolveSprite(modelElementFace.textureId); + if (spriteIdentifier.texture == MissingSprite.getMissingSpriteId()) { + unresolvedTextureReferences.add(Pair.of(modelElementFace.textureId, this.id)); + } + set2.add(spriteIdentifier); + } + } + + this.overrides.forEach((modelOverride) -> { + UnbakedModel unbakedModel = unbakedModelGetter.apply(modelOverride.getModelId()); + if (!Objects.equals(unbakedModel, this)) { + set2.addAll(unbakedModel.getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences)); + } + }); + if (this.getRootModel() == ModelLoader.GENERATION_MARKER) { + ItemModelGenerator.LAYERS.forEach((string) -> set2.add(this.resolveSprite(string))); + } + + return set2; + } + @Override public BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) { return this.bake(baker, this, textureGetter, rotationContainer, modelId, true); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java index 2ea1a48da..ff143898c 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java @@ -3,6 +3,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.*; +import com.mojang.datafixers.util.Pair; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.block.Block; @@ -69,6 +70,11 @@ public void setParents(Function modelLoader) { this.getComponents().forEach(component -> component.getModel().setParents(modelLoader)); } + @Override + public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { + return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + } + @Nullable public BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) { MultipartBakedModel.Builder builder = new MultipartBakedModel.Builder(); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java index 0d83ecd0b..bc0d63a80 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import com.google.gson.*; +import com.mojang.datafixers.util.Pair; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.modificationstation.stationapi.api.client.render.model.*; @@ -13,6 +14,7 @@ import java.lang.reflect.Type; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -52,6 +54,11 @@ public void setParents(Function modelLoader) { this.getVariants().stream().map(ModelVariant::getLocation).distinct().forEach(id -> modelLoader.apply(id).setParents(modelLoader)); } + @Override + public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { + return this.getVariants().stream().map(ModelVariant::getLocation).distinct().flatMap((identifier) -> unbakedModelGetter.apply(identifier).getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + } + @Nullable public BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) { if (this.getVariants().isEmpty()) { From 558b603145a36805dc7e2a5b16e7aafdc387019c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 21:11:02 +0200 Subject: [PATCH 07/13] Code stealing moment --- .../api/client/render/model/json/JsonUnbakedModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index 1511ba4ca..8a303ae8e 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -144,7 +144,7 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { + public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { Set set = Sets.newLinkedHashSet(); for(JsonUnbakedModel JsonModel = this; JsonModel.parentId != null && JsonModel.parent == null; JsonModel = JsonModel.parent) { From 2bc5b1037ad19f18136c32e857a37dd5e34920b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 21:28:55 +0200 Subject: [PATCH 08/13] Fix getTextureDependencies --- .../render/model/json/JsonUnbakedModel.java | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index 8a303ae8e..01cb3bd27 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -144,57 +144,59 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { + public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { Set set = Sets.newLinkedHashSet(); - for(JsonUnbakedModel JsonModel = this; JsonModel.parentId != null && JsonModel.parent == null; JsonModel = JsonModel.parent) { - set.add(JsonModel); - UnbakedModel unbakedModel = unbakedModelGetter.apply(JsonModel.parentId); - if (unbakedModel == null) { - LOGGER.warn("No parent '{}' while loading model '{}'", this.parentId, JsonModel); - } - + JsonUnbakedModel jsonUnbakedModel = this; + while (jsonUnbakedModel.parentId != null && jsonUnbakedModel.parent == null) { + set.add(jsonUnbakedModel); + + UnbakedModel unbakedModel = modelLoader.apply(jsonUnbakedModel.parentId); + if (unbakedModel == null) + LOGGER.warn("No parent '{}' while loading model '{}'", this.parentId, jsonUnbakedModel); if (set.contains(unbakedModel)) { - LOGGER.warn("Found 'parent' loop while loading model '{}' in chain: {} -> {}", JsonModel, set.stream().map(Object::toString).collect(Collectors.joining(" -> ")), this.parentId); + LOGGER.warn("Found 'parent' loop while loading model '{}' in chain: {} -> {}", jsonUnbakedModel, set.stream().map(Object::toString).collect(Collectors.joining(" -> ")), this.parentId); unbakedModel = null; } - + if (unbakedModel == null) { - JsonModel.parentId = ModelLoader.MISSING_ID.asIdentifier(); - unbakedModel = unbakedModelGetter.apply(JsonModel.parentId); + unbakedModel = modelLoader.apply(ModelLoader.MISSING_ID.asIdentifier()); } - - if (!(unbakedModel instanceof JsonUnbakedModel jsonModel)) { + + if (!(unbakedModel instanceof JsonUnbakedModel)) { throw new IllegalStateException("BlockModel parent has to be a block model."); } - - JsonModel.parent = jsonModel; + + jsonUnbakedModel = jsonUnbakedModel.parent; } - Set set2 = Sets.newHashSet(this.resolveSprite("particle")); + Set dependencies = Sets.newHashSet(this.resolveSprite("particle")); for (ModelElement modelElement : this.getElements()) { SpriteIdentifier spriteIdentifier; for (ModelElementFace modelElementFace : modelElement.faces.values()) { spriteIdentifier = this.resolveSprite(modelElementFace.textureId); + if (spriteIdentifier.texture == MissingSprite.getMissingSpriteId()) { unresolvedTextureReferences.add(Pair.of(modelElementFace.textureId, this.id)); } - set2.add(spriteIdentifier); + + dependencies.add(spriteIdentifier); } } this.overrides.forEach((modelOverride) -> { - UnbakedModel unbakedModel = unbakedModelGetter.apply(modelOverride.getModelId()); - if (!Objects.equals(unbakedModel, this)) { - set2.addAll(unbakedModel.getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences)); + UnbakedModel overrideModel = modelLoader.apply(modelOverride.getModelId()); + if (!Objects.equals(overrideModel, this)) { + dependencies.addAll(overrideModel.getTextureDependencies(modelLoader, unresolvedTextureReferences)); } }); + if (this.getRootModel() == ModelLoader.GENERATION_MARKER) { - ItemModelGenerator.LAYERS.forEach((string) -> set2.add(this.resolveSprite(string))); + ItemModelGenerator.LAYERS.forEach((string) -> dependencies.add(this.resolveSprite(string))); } - return set2; + return dependencies; } @Override From 71e405941857d663d61013557a471c945a1506f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 21:29:47 +0200 Subject: [PATCH 09/13] proper names --- .../stationapi/api/client/render/model/UnbakedModel.java | 2 +- .../api/client/render/model/json/MultipartUnbakedModel.java | 4 ++-- .../api/client/render/model/json/WeightedUnbakedModel.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java index 7bf06a0cb..066da58d9 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java @@ -18,7 +18,7 @@ public interface UnbakedModel { void setParents(Function parents); - Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences); + Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences); @Nullable BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java index ff143898c..b136f7129 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java @@ -71,8 +71,8 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { - return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { + return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextureDependencies(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); } @Nullable diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java index bc0d63a80..6fa4f5aad 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java @@ -55,8 +55,8 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function unbakedModelGetter, Set> unresolvedTextureReferences) { - return this.getVariants().stream().map(ModelVariant::getLocation).distinct().flatMap((identifier) -> unbakedModelGetter.apply(identifier).getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { + return this.getVariants().stream().map(ModelVariant::getLocation).distinct().flatMap((identifier) -> modelLoader.apply(identifier).getTextureDependencies(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); } @Nullable From ca0c7ae341e90d361ad5a8b6f25a778b3fc22c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Mon, 29 Jun 2026 22:09:15 +0200 Subject: [PATCH 10/13] Reduced to a helper method --- .../api/client/render/model/UnbakedModel.java | 2 +- .../render/model/json/JsonUnbakedModel.java | 42 +++---------------- .../model/json/MultipartUnbakedModel.java | 4 +- .../model/json/WeightedUnbakedModel.java | 4 +- 4 files changed, 10 insertions(+), 42 deletions(-) diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java index 066da58d9..0101d4bf3 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java @@ -18,7 +18,7 @@ public interface UnbakedModel { void setParents(Function parents); - Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences); + Collection getTextures(Function modelLoader, Set> unresolvedTextureReferences); @Nullable BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index 01cb3bd27..31ed4846c 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -144,33 +144,8 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { - Set set = Sets.newLinkedHashSet(); - - JsonUnbakedModel jsonUnbakedModel = this; - while (jsonUnbakedModel.parentId != null && jsonUnbakedModel.parent == null) { - set.add(jsonUnbakedModel); - - UnbakedModel unbakedModel = modelLoader.apply(jsonUnbakedModel.parentId); - if (unbakedModel == null) - LOGGER.warn("No parent '{}' while loading model '{}'", this.parentId, jsonUnbakedModel); - if (set.contains(unbakedModel)) { - LOGGER.warn("Found 'parent' loop while loading model '{}' in chain: {} -> {}", jsonUnbakedModel, set.stream().map(Object::toString).collect(Collectors.joining(" -> ")), this.parentId); - unbakedModel = null; - } - - if (unbakedModel == null) { - unbakedModel = modelLoader.apply(ModelLoader.MISSING_ID.asIdentifier()); - } - - if (!(unbakedModel instanceof JsonUnbakedModel)) { - throw new IllegalStateException("BlockModel parent has to be a block model."); - } - - jsonUnbakedModel = jsonUnbakedModel.parent; - } - - Set dependencies = Sets.newHashSet(this.resolveSprite("particle")); + public Collection getTextures(Function modelLoader, Set> unresolvedTextureReferences) { + Set textures = Sets.newHashSet(this.resolveSprite("particle")); for (ModelElement modelElement : this.getElements()) { SpriteIdentifier spriteIdentifier; @@ -181,22 +156,15 @@ public Collection getTextureDependencies(Function { - UnbakedModel overrideModel = modelLoader.apply(modelOverride.getModelId()); - if (!Objects.equals(overrideModel, this)) { - dependencies.addAll(overrideModel.getTextureDependencies(modelLoader, unresolvedTextureReferences)); - } - }); - if (this.getRootModel() == ModelLoader.GENERATION_MARKER) { - ItemModelGenerator.LAYERS.forEach((string) -> dependencies.add(this.resolveSprite(string))); + ItemModelGenerator.LAYERS.forEach((string) -> textures.add(this.resolveSprite(string))); } - return dependencies; + return textures; } @Override diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java index b136f7129..50a1aac50 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/MultipartUnbakedModel.java @@ -71,8 +71,8 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { - return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextureDependencies(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + public Collection getTextures(Function modelLoader, Set> unresolvedTextureReferences) { + return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextures(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); } @Nullable diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java index 6fa4f5aad..43561b3b4 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/WeightedUnbakedModel.java @@ -55,8 +55,8 @@ public void setParents(Function modelLoader) { } @Override - public Collection getTextureDependencies(Function modelLoader, Set> unresolvedTextureReferences) { - return this.getVariants().stream().map(ModelVariant::getLocation).distinct().flatMap((identifier) -> modelLoader.apply(identifier).getTextureDependencies(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); + public Collection getTextures(Function modelLoader, Set> unresolvedTextureReferences) { + return this.getVariants().stream().map(ModelVariant::getLocation).distinct().flatMap((identifier) -> modelLoader.apply(identifier).getTextures(modelLoader, unresolvedTextureReferences).stream()).collect(Collectors.toSet()); } @Nullable From d1b10b30bd16ed6146531e1e8d33fc8c635a4d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Tue, 30 Jun 2026 21:25:09 +0200 Subject: [PATCH 11/13] More changes, more events and mine's beloved SuperBuilder --- .../sltest/render/ModelLoadingListener.java | 17 ++++++++ .../model/BeforeModelLoaderInitEvent.java | 20 +++++++++ .../render/model/BlockStateReloadEvent.java | 9 +--- .../model/ModelVariantMapOverrideEvent.java | 42 +++++++++++++++++++ .../UnbakedModelLoadingFinishedEvent.java | 25 ++++------- .../render/model/BakedModelManager.java | 2 +- .../api/client/render/model/ModelLoader.java | 32 ++++++++------ .../render/model/json/JsonUnbakedModel.java | 2 +- 8 files changed, 110 insertions(+), 39 deletions(-) create mode 100644 station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BeforeModelLoaderInitEvent.java create mode 100644 station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java diff --git a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java index 82d808214..261b1fde0 100644 --- a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java +++ b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java @@ -1,9 +1,13 @@ package net.modificationstation.sltest.render; +import com.mojang.datafixers.util.Pair; import net.mine_diver.unsafeevents.listener.EventListener; import net.modificationstation.sltest.SLTest; +import net.modificationstation.stationapi.api.client.event.render.model.BeforeModelLoaderInitEvent; import net.modificationstation.stationapi.api.client.event.render.model.BlockStateReloadEvent; +import net.modificationstation.stationapi.api.client.event.render.model.ModelVariantMapOverrideEvent; import net.modificationstation.stationapi.api.client.event.render.model.UnbakedModelLoadingFinishedEvent; +import net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap; import net.modificationstation.stationapi.api.util.Identifier; public class ModelLoadingListener { @@ -24,4 +28,17 @@ public void modelsLoaded(UnbakedModelLoadingFinishedEvent event) { public void injectBlockState(BlockStateReloadEvent event) { event.addBlockstateJsonString(Identifier.of("minecraft:sponge"), blockstateJson); } + + @EventListener + public void beforeLoader(BeforeModelLoaderInitEvent event) { + SLTest.LOGGER.info("Model Loader Init"); + } + + @EventListener + public void mineLdiver(ModelVariantMapOverrideEvent event) { + System.err.println(event.id); + for (Pair variantMap : event.variantMaps) { + System.err.println(" - " + variantMap.getFirst() + " | " + variantMap.getSecond().getVariantMap().toString()); + } + } } diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BeforeModelLoaderInitEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BeforeModelLoaderInitEvent.java new file mode 100644 index 000000000..89e87f242 --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BeforeModelLoaderInitEvent.java @@ -0,0 +1,20 @@ +package net.modificationstation.stationapi.api.client.event.render.model; + +import lombok.experimental.SuperBuilder; +import net.mine_diver.unsafeevents.Event; +import net.modificationstation.stationapi.api.client.color.block.BlockColors; +import net.modificationstation.stationapi.api.client.render.model.ModelLoader; +import net.modificationstation.stationapi.api.client.render.model.UnbakedModel; +import net.modificationstation.stationapi.api.util.Identifier; + +import java.util.List; +import java.util.Map; + +@SuperBuilder +public final class BeforeModelLoaderInitEvent extends Event { + public final ModelLoader modelLoader; + public final Map unbakedModels; + public final Map modelsToBake; + public final Map> blockStates; + public final BlockColors blockColors; +} diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java index 5ba22657d..28a779424 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java @@ -1,6 +1,7 @@ package net.modificationstation.stationapi.api.client.event.render.model; import com.mojang.datafixers.util.Pair; +import lombok.experimental.SuperBuilder; import net.mine_diver.unsafeevents.Event; import net.mine_diver.unsafeevents.event.EventPhases; import net.modificationstation.stationapi.api.StationAPI; @@ -21,18 +22,12 @@ /** * An event which is fired when block states are being reloaded */ -@EventPhases(StationAPI.INTERNAL_PHASE) +@SuperBuilder public class BlockStateReloadEvent extends Event { public final ResourceManager resourceManager; public final Executor executor; public final List>>> states; - public BlockStateReloadEvent(ResourceManager resourceManager, Executor executor, List>>> states) { - this.resourceManager = resourceManager; - this.executor = executor; - this.states = states; - } - /** * Adds a blockstate json to the list of blockstates to be loaded * diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java new file mode 100644 index 000000000..a2e6a624e --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java @@ -0,0 +1,42 @@ +package net.modificationstation.stationapi.api.client.event.render.model; + +import com.mojang.datafixers.util.Pair; +import lombok.experimental.SuperBuilder; +import net.mine_diver.unsafeevents.Event; +import net.minecraft.block.Block; +import net.modificationstation.stationapi.api.block.BlockState; +import net.modificationstation.stationapi.api.client.render.model.ModelLoader; +import net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap; +import net.modificationstation.stationapi.api.util.Identifier; +import net.modificationstation.stationapi.api.util.Namespace; + +import java.util.List; + +/** + * Fired after the {@link ModelVariantMap} is deserialized, allowing to add custom overrides + */ +@SuperBuilder +public final class ModelVariantMapOverrideEvent extends Event { + /** + * The model loader that is loading this blockstate + */ + public final ModelLoader modelLoader; + /** + * The {@link Identifier} of the {@link Block} for which the {@link BlockState} is being loaded + */ + public final Identifier id; + /** + * The map of the variants of the {@link BlockState} + */ + public final List> variantMaps; + + /** + * Adds a custom {@link ModelVariantMap} to the list of overrides for the given {@link BlockState} + * + * @param namespace The {@link Namespace} of your mod + * @param variantMap The {@link ModelVariantMap} to add + */ + public void addModelVariantMap(Namespace namespace, ModelVariantMap variantMap) { + this.variantMaps.add(new Pair<>(namespace.getName() + " Model Variant Event Override", variantMap)); + } +} diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java index fada8e564..a5c6871b0 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.java @@ -1,8 +1,7 @@ package net.modificationstation.stationapi.api.client.event.render.model; +import lombok.experimental.SuperBuilder; import net.mine_diver.unsafeevents.Event; -import net.mine_diver.unsafeevents.event.EventPhases; -import net.modificationstation.stationapi.api.StationAPI; import net.modificationstation.stationapi.api.client.color.block.BlockColors; import net.modificationstation.stationapi.api.client.render.model.ModelLoader; import net.modificationstation.stationapi.api.client.render.model.UnbakedModel; @@ -11,19 +10,11 @@ import java.util.List; import java.util.Map; -@EventPhases(StationAPI.INTERNAL_PHASE) -public class UnbakedModelLoadingFinishedEvent extends Event { - public ModelLoader modelLoader; - public Map unbakedModels; - public Map modelsToBake; - public Map> blockStates; - public BlockColors blockColors; - - public UnbakedModelLoadingFinishedEvent(ModelLoader modelLoader, Map unbakedModels, Map modelsToBake, Map> blockStates, BlockColors blockColors) { - this.modelLoader = modelLoader; - this.unbakedModels = unbakedModels; - this.modelsToBake = modelsToBake; - this.blockStates = blockStates; - this.blockColors = blockColors; - } +@SuperBuilder +public final class UnbakedModelLoadingFinishedEvent extends Event { + public final ModelLoader modelLoader; + public final Map unbakedModels; + public final Map modelsToBake; + public final Map> blockStates; + public final BlockColors blockColors; } diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java index a9abc82e4..df026a5d1 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/BakedModelManager.java @@ -131,7 +131,7 @@ private static CompletableFuture> reloadModels private static CompletableFuture>> reloadBlockStates(ResourceManager resourceManager, Executor executor) { return CompletableFuture.supplyAsync(() -> ModelLoader.BLOCK_STATES_FINDER.findAllResources(resourceManager), executor).thenCompose(blockStates2 -> { List>>> list = new ArrayList<>(blockStates2.size()); - StationAPI.EVENT_BUS.post(new BlockStateReloadEvent(resourceManager, executor, list)); + StationAPI.EVENT_BUS.post(BlockStateReloadEvent.builder().resourceManager(resourceManager).executor(executor).states(list).build()); for (Map.Entry> entry : blockStates2.entrySet()) { list.add(CompletableFuture.supplyAsync(() -> { List resources = entry.getValue(); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java index 3833f1fa9..ee7898a72 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java @@ -16,9 +16,7 @@ import net.modificationstation.stationapi.api.block.BlockState; import net.modificationstation.stationapi.api.block.BlockStateHolder; import net.modificationstation.stationapi.api.client.color.block.BlockColors; -import net.modificationstation.stationapi.api.client.event.render.model.LoadUnbakedModelEvent; -import net.modificationstation.stationapi.api.client.event.render.model.UnbakedModelLoadingFinishedEvent; -import net.modificationstation.stationapi.api.client.event.render.model.PreLoadUnbakedModelEvent; +import net.modificationstation.stationapi.api.client.event.render.model.*; import net.modificationstation.stationapi.api.client.render.block.BlockModels; import net.modificationstation.stationapi.api.client.render.model.json.JsonUnbakedModel; import net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap; @@ -88,7 +86,10 @@ public ModelLoader(BlockColors blockColors, Profiler profiler, Map model.setParents(this::getOrLoadModel)); + profiler.swap("after_init_event"); + StationAPI.EVENT_BUS.post(UnbakedModelLoadingFinishedEvent.builder().modelLoader(this).unbakedModels(this.unbakedModels).modelsToBake(this.modelsToBake).blockStates(this.blockStates).blockColors(this.blockColors).build()); + profiler.pop(); - StationAPI.EVENT_BUS.post(new UnbakedModelLoadingFinishedEvent(this, this.unbakedModels, this.modelsToBake, this.blockStates, this.blockColors)); } public void bake(BiFunction spriteLoader) { @@ -222,10 +225,10 @@ private void loadModel(Identifier id) throws Exception { } else { StateManager stateManager = /*Optional.ofNullable(STATIC_DEFINITIONS.get(identifier)).orElseGet(() -> */((BlockStateHolder) Objects.requireNonNull(BlockRegistry.INSTANCE.get(modelIdentifier.id))).getStateManager()/*)*/; variantMapDeserializationContext.setStateFactory(stateManager); - ImmutableList> list = ImmutableList.copyOf(this.blockColors.getProperties(stateManager.getOwner())); - ImmutableList immutableList = stateManager.getStates(); - Map map = new IdentityHashMap<>(); - immutableList.forEach(state -> map.put(BlockModels.getModelId(modelIdentifier.id, state), state)); + ImmutableList> blockColors = ImmutableList.copyOf(this.blockColors.getProperties(stateManager.getOwner())); + ImmutableList states = stateManager.getStates(); + Map idToStateMap = new IdentityHashMap<>(); + states.forEach(state -> idToStateMap.put(BlockModels.getModelId(modelIdentifier.id, state), state)); Map>> map2 = new HashMap<>(); Identifier identifier2 = BLOCK_STATES_FINDER.toResourcePath(modelIdentifier.id); UnbakedModel unbakedModel = VANILLA_MARKER; @@ -239,18 +242,21 @@ private void loadModel(Identifier id) throws Exception { throw new ModelLoaderException(String.format(Locale.ROOT, "Exception loading blockstate definition: '%s' in texturepack: '%s': %s", identifier2, blockState.source, var4.getMessage())); } }).toList(); + + StationAPI.EVENT_BUS.post(ModelVariantMapOverrideEvent.builder().modelLoader(this).id(modelIdentifier.id).variantMaps(list2).build()); + for (Pair pair2 : list2) { MultipartUnbakedModel multipartUnbakedModel; ModelVariantMap modelVariantMap = pair2.getSecond(); Map>> map4 = new IdentityHashMap<>(); if (modelVariantMap.hasMultipartModel()) { multipartUnbakedModel = modelVariantMap.getMultipartModel(); - immutableList.forEach(blockState -> map4.put(blockState, Pair.of(multipartUnbakedModel, () -> ModelDefinition.create(blockState, multipartUnbakedModel, list)))); + states.forEach(blockState -> map4.put(blockState, Pair.of(multipartUnbakedModel, () -> ModelDefinition.create(blockState, multipartUnbakedModel, blockColors)))); } else multipartUnbakedModel = null; modelVariantMap.getVariantMap().forEach((string, weightedUnbakedModel) -> { try { - immutableList.stream().filter(ModelLoader.stateKeyToPredicate(stateManager, string)).forEach(blockState -> { - Pair> pair3 = map4.put(blockState, Pair.of(weightedUnbakedModel, () -> ModelDefinition.create(blockState, weightedUnbakedModel, list))); + states.stream().filter(ModelLoader.stateKeyToPredicate(stateManager, string)).forEach(blockState -> { + Pair> pair3 = map4.put(blockState, Pair.of(weightedUnbakedModel, () -> ModelDefinition.create(blockState, weightedUnbakedModel, blockColors))); if (pair3 != null && pair3.getFirst() != multipartUnbakedModel) { map4.put(blockState, pair); throw new RuntimeException("Overlapping definition with: " + modelVariantMap.getVariantMap().entrySet().stream().filter(entry -> entry.getValue() == pair3.getFirst()).findFirst().orElseThrow(NullPointerException::new).getKey()); @@ -268,7 +274,7 @@ private void loadModel(Identifier id) throws Exception { throw new ModelLoaderException(String.format("Exception loading blockstate definition: '%s': %s", identifier2, exception)); } finally { Map> map6 = new HashMap<>(); - map.forEach((id1, blockState) -> { + idToStateMap.forEach((id1, blockState) -> { Pair> pair2 = map2.get(blockState); // Vanilla blocks don't have JSON models, and we don't want to enforce them // LOGGER.warn("Exception loading blockstate definition: '{}' missing model for variant: '{}'", identifier2, id1); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java index 31ed4846c..2c1b74d36 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/json/JsonUnbakedModel.java @@ -30,7 +30,7 @@ import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; -public final class JsonUnbakedModel implements UnbakedModel { +public class JsonUnbakedModel implements UnbakedModel { public static final Identifier BUILTIN_GENERATED = Identifier.of("builtin/generated"); private static final BakedQuadFactory QUAD_FACTORY = new BakedQuadFactory(); From c97849c5da1ca880437c31eecc4f529042eadb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Tue, 30 Jun 2026 21:56:16 +0200 Subject: [PATCH 12/13] Fix AMI yelling --- .../net/modificationstation/sltest/item/ItemListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/modificationstation/sltest/item/ItemListener.java b/src/test/java/net/modificationstation/sltest/item/ItemListener.java index 4a0fb4446..54c78c67e 100644 --- a/src/test/java/net/modificationstation/sltest/item/ItemListener.java +++ b/src/test/java/net/modificationstation/sltest/item/ItemListener.java @@ -35,8 +35,8 @@ public void registerItems(ItemRegistryEvent event) { testPickaxe = new ModdedPickaxeItem(NAMESPACE.id("test_pickaxe"), testMaterial); //8476 testNBTItem = new NBTItem(NAMESPACE.id("nbt_item")); //8477 testModelItem = new ModelItem(NAMESPACE.id("model_item")).setMaxCount(1); - ironOre = event.register(NAMESPACE.id("iron_ore"), new Item(ItemRegistry.AUTO_ID)); - generatedItem = event.register(NAMESPACE.id("generated_item"), new Item(ItemRegistry.AUTO_ID)); + ironOre = event.register(NAMESPACE.id("iron_ore"), new Item(ItemRegistry.AUTO_ID).setTranslationKey(NAMESPACE.id("iron_ore"))); + generatedItem = event.register(NAMESPACE.id("generated_item"), new Item(ItemRegistry.AUTO_ID).setTranslationKey(NAMESPACE.id("generated_item"))); variationBlockIdle = new BlockStateItem(NAMESPACE.id("variation_block_idle"), Blocks.VARIATION_BLOCK.get().getDefaultState()); variationBlockPassive = new BlockStateItem(NAMESPACE.id("variation_block_passive"), Blocks.VARIATION_BLOCK.get().getDefaultState().with(VariationBlock.VARIANT, VariationBlock.Variant.PASSIVE)); variationBlockActive = new BlockStateItem(NAMESPACE.id("variation_block_active"), Blocks.VARIATION_BLOCK.get().getDefaultState().with(VariationBlock.VARIANT, VariationBlock.Variant.ACTIVE)); From 317389d5d9e82bf6bb35b9cbbc18b412015099e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Fild=C3=A1n?= Date: Tue, 30 Jun 2026 22:03:20 +0200 Subject: [PATCH 13/13] Finish ModelVariantMapOverrideEvent --- .../sltest/render/ModelLoadingListener.java | 18 +++++++++++++++++- .../model/ModelVariantMapOverrideEvent.java | 11 +++++++++++ .../api/client/render/model/ModelLoader.java | 6 +++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java index 261b1fde0..c418cfa54 100644 --- a/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java +++ b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java @@ -10,6 +10,9 @@ import net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap; import net.modificationstation.stationapi.api.util.Identifier; +import java.io.BufferedReader; +import java.io.StringReader; + public class ModelLoadingListener { @EventListener public void modelsLoaded(UnbakedModelLoadingFinishedEvent event) { @@ -32,10 +35,23 @@ public void injectBlockState(BlockStateReloadEvent event) { @EventListener public void beforeLoader(BeforeModelLoaderInitEvent event) { SLTest.LOGGER.info("Model Loader Init"); - } + } + + String blockstateVariantJson = (""" + { + "variants": { + "": {"model": "sltest:block/test_block"} + } + }""" + ); @EventListener public void mineLdiver(ModelVariantMapOverrideEvent event) { + if (event.id.equals(Identifier.of("minecraft:note_block"))) { + BufferedReader reader = new BufferedReader(new StringReader(blockstateVariantJson)); + event.addModelVariantMap(SLTest.NAMESPACE, ModelVariantMap.fromJson(event.deserializationContext, reader)); + } + System.err.println(event.id); for (Pair variantMap : event.variantMaps) { System.err.println(" - " + variantMap.getFirst() + " | " + variantMap.getSecond().getVariantMap().toString()); diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java index a2e6a624e..e51046b00 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java @@ -7,11 +7,14 @@ import net.modificationstation.stationapi.api.block.BlockState; import net.modificationstation.stationapi.api.client.render.model.ModelLoader; import net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap; +import net.modificationstation.stationapi.api.state.StateManager; import net.modificationstation.stationapi.api.util.Identifier; import net.modificationstation.stationapi.api.util.Namespace; import java.util.List; +import static net.modificationstation.stationapi.api.client.render.model.json.ModelVariantMap.*; + /** * Fired after the {@link ModelVariantMap} is deserialized, allowing to add custom overrides */ @@ -29,6 +32,14 @@ public final class ModelVariantMapOverrideEvent extends Event { * The map of the variants of the {@link BlockState} */ public final List> variantMaps; + /** + * The {@link StateManager} of the {@link Block} for which the {@link BlockState} is being loaded. + */ + public final StateManager stateManager; + /** + * The {@link ModelVariantMap.DeserializationContext} for deserializing the {@link ModelVariantMap} + */ + public final DeserializationContext deserializationContext; /** * Adds a custom {@link ModelVariantMap} to the list of overrides for the given {@link BlockState} diff --git a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java index ee7898a72..aa49ad4b9 100644 --- a/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/ModelLoader.java @@ -235,15 +235,15 @@ private void loadModel(Identifier id) throws Exception { ModelDefinition modelDefinition2 = new ModelDefinition(ImmutableList.of(unbakedModel), ImmutableList.of()); Pair> pair = Pair.of(unbakedModel, () -> modelDefinition2); try { - List> list2 = this.blockStates.getOrDefault(identifier2, List.of()).stream().map((blockState) -> { + List> list2 = new ArrayList<>(this.blockStates.getOrDefault(identifier2, List.of()).stream().map((blockState) -> { try { return Pair.of(blockState.source, ModelVariantMap.fromJson(variantMapDeserializationContext, blockState.data)); } catch (Exception var4) { throw new ModelLoaderException(String.format(Locale.ROOT, "Exception loading blockstate definition: '%s' in texturepack: '%s': %s", identifier2, blockState.source, var4.getMessage())); } - }).toList(); + }).toList()); - StationAPI.EVENT_BUS.post(ModelVariantMapOverrideEvent.builder().modelLoader(this).id(modelIdentifier.id).variantMaps(list2).build()); + StationAPI.EVENT_BUS.post(ModelVariantMapOverrideEvent.builder().modelLoader(this).id(modelIdentifier.id).variantMaps(list2).stateManager(stateManager).deserializationContext(variantMapDeserializationContext).build()); for (Pair pair2 : list2) { MultipartUnbakedModel multipartUnbakedModel;