diff options
5 files changed, 121 insertions, 3 deletions
diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt index 7abdaf7..11528fd 100644 --- a/src/main/kotlin/events/CustomItemModelEvent.kt +++ b/src/main/kotlin/events/CustomItemModelEvent.kt @@ -18,6 +18,6 @@ data class CustomItemModelEvent( } fun overrideIfExists(overrideModel: Identifier) { - TODO() + this.overrideModel = overrideModel } } diff --git a/src/texturePacks/README.md b/src/texturePacks/README.md new file mode 100644 index 0000000..8932817 --- /dev/null +++ b/src/texturePacks/README.md @@ -0,0 +1,13 @@ +<!-- +SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> + +SPDX-License-Identifier: CC0-1.0 +--> + +# Technical Notes for the texture pack implementation + +Relevant classes: + +`ItemModelManager` can be used to select an `ItemModel`. This is done from the `ITEM_MODEL` component which is defaulted by the `Item` class. + +The list of available `ItemModel`s (as in `Identifier` -> `ItemModel` maps) is loaded by `BakedModelManager`. To this end, item models in particular are loaded from `ItemAssetsLoader#load`. Those `ItemAssets` are found in `assets/<ns>/items/` directly (not in the model folder) and can be used to select other models, similar to how predicates used to work diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt index 84c04af..85dfa32 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomGlobalArmorOverrides.kt @@ -107,7 +107,7 @@ object CustomGlobalArmorOverrides { return model } else if (layers != null) { val idNumber = sentinelFirmRunning.incrementAndGet() - val identifier = Identifier.of("firmament:sentinel/$idNumber") + val identifier = Identifier.of("firmament:sentinel/armor/$idNumber") val equipmentLayers = layers.map { EquipmentModel.Layer( it.identifier, if (it.tint) { diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java index 0863caa..ffa23f7 100644 --- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java @@ -5,20 +5,46 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import moe.nea.firmament.events.CustomItemModelEvent; import net.minecraft.client.item.ItemModelManager; +import net.minecraft.client.render.item.model.ItemModel; +import net.minecraft.client.render.item.model.MissingItemModel; +import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.component.ComponentType; import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.Function; @Mixin(ItemModelManager.class) public class ReplaceItemModelPatch { + @Shadow + @Final + private Function<Identifier, ItemModel> modelGetter; + + @Inject(method = "<init>", at = @At("TAIL")) + private void saveMissingModel(BakedModelManager bakedModelManager, CallbackInfo ci) { + } + + @Unique + // TODO: Fix scissors + private boolean hasModel(Identifier identifier) { + return !(modelGetter.apply(identifier) instanceof MissingItemModel); + } + @WrapOperation( method = "update(Lnet/minecraft/client/render/item/ItemRenderState;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;")) private Object replaceItemModelByIdentifier(ItemStack instance, ComponentType componentType, Operation<Object> original) { var override = CustomItemModelEvent.getModelIdentifier(instance); - if (override != null) + if (override != null && hasModel(override)) { return override; + } return original.call(instance, componentType); } } diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java new file mode 100644 index 0000000..3f4cc44 --- /dev/null +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/SupplyFakeModelPatch.java @@ -0,0 +1,79 @@ +package moe.nea.firmament.mixins.custommodels; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.client.item.ItemAsset; +import net.minecraft.client.item.ItemAssetsLoader; +import net.minecraft.client.render.item.model.BasicItemModel; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourcePack; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +@Mixin(ItemAssetsLoader.class) +public class SupplyFakeModelPatch { + + @ModifyReturnValue( + method = "load", + at = @At("RETURN") + ) + private static CompletableFuture<ItemAssetsLoader.Result> injectFakeGeneratedModels( + CompletableFuture<ItemAssetsLoader.Result> original, + @Local(argsOnly = true) ResourceManager resourceManager, + @Local(argsOnly = true) Executor executor + ) { + return original.thenCompose(oldModels -> CompletableFuture.supplyAsync(() -> supplyExtraModels(resourceManager, oldModels), executor)); + } + + private static ItemAssetsLoader.Result supplyExtraModels(ResourceManager resourceManager, ItemAssetsLoader.Result oldModels) { + Map<Identifier, ItemAsset> newModels = new HashMap<>(oldModels.contents()); + var resources = resourceManager.findResources( + "models/item", + id -> id.getNamespace().equals("firmskyblock") + && id.getPath().endsWith(".json") + && !id.getPath().substring("models/item/".length()).contains("/")); + for (Map.Entry<Identifier, Resource> model : resources.entrySet()) { + var resource = model.getValue(); + var itemModelId = model.getKey().withPath(it -> it.substring("models/item/".length(), it.length() - ".json".length())); + // TODO: parse json file here and make use of it in order to generate predicate files. + var genericModelId = itemModelId.withPrefixedPath("item/"); + if (resourceManager.getResource(itemModelId) + .map(Resource::getPack) + .map(it -> isResourcePackNewer(resourceManager, it, resource.getPack())) + .orElse(true)) { + newModels.put(itemModelId, new ItemAsset( + new BasicItemModel.Unbaked(genericModelId, List.of()), + new ItemAsset.Properties(true) + )); + } + } + return new ItemAssetsLoader.Result(newModels); + } + + private static boolean isResourcePackNewer( + ResourceManager manager, + ResourcePack null_, ResourcePack proposal) { + var pack = manager.streamResourcePacks() + .filter(it -> it == null_ || it == proposal) + .collect(findLast()); + return pack.orElse(null) == proposal; + } + + private static <T> Collector<T, ?, Optional<T>> findLast() { + return Collectors.reducing(Optional.empty(), Optional::of, + (left, right) -> right.isPresent() ? right : left); + + } + +} |