diff --git a/build.gradle.kts b/build.gradle.kts index 3b94e16e2..007c1fa24 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -144,6 +144,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" } 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)); 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..c418cfa54 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/render/ModelLoadingListener.java @@ -0,0 +1,60 @@ +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; + +import java.io.BufferedReader; +import java.io.StringReader; + +public class ModelLoadingListener { + @EventListener + public void modelsLoaded(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); + } + + @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/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-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-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; } 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 new file mode 100644 index 000000000..28a779424 --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/BlockStateReloadEvent.java @@ -0,0 +1,92 @@ +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; +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 + */ +@SuperBuilder +public class BlockStateReloadEvent extends Event { + public final ResourceManager resourceManager; + public final Executor executor; + public final List>>> 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/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..e51046b00 --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/ModelVariantMapOverrideEvent.java @@ -0,0 +1,53 @@ +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.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 + */ +@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; + /** + * 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} + * + * @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 new file mode 100644 index 000000000..a5c6871b0 --- /dev/null +++ b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/event/render/model/UnbakedModelLoadingFinishedEvent.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 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 aea0398ca..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 @@ -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(BlockStateReloadEvent.builder().resourceManager(resourceManager).executor(executor).states(list).build()); + 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))); }); } @@ -187,7 +191,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..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 @@ -16,8 +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.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; @@ -55,7 +54,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,7 +85,11 @@ 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.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(); } @@ -215,35 +225,38 @@ 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; 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).stateManager(stateManager).deserializationContext(variantMapDeserializationContext).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()); @@ -261,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/UnbakedModel.java b/station-renderer-api-v0/src/main/java/net/modificationstation/stationapi/api/client/render/model/UnbakedModel.java index 768599d93..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 @@ -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 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/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..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 @@ -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; @@ -28,8 +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(); @@ -94,6 +95,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()); @@ -130,6 +143,30 @@ public void setParents(Function modelLoader) { }); } + @Override + public Collection getTextures(Function modelLoader, Set> unresolvedTextureReferences) { + Set textures = 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)); + } + + textures.add(spriteIdentifier); + } + } + + if (this.getRootModel() == ModelLoader.GENERATION_MARKER) { + ItemModelGenerator.LAYERS.forEach((string) -> textures.add(this.resolveSprite(string))); + } + + return textures; + } + @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..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 @@ -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 getTextures(Function modelLoader, Set> unresolvedTextureReferences) { + return this.getComponents().stream().flatMap((multipartModelComponent) -> multipartModelComponent.getModel().getTextures(modelLoader, 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..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 @@ -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 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 public BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) { if (this.getVariants().isEmpty()) {