Skip to content

Commit

Permalink
WIP: Automatically generate fake item asset entries for skyblock items
Browse files Browse the repository at this point in the history
  • Loading branch information
nea89o committed Dec 9, 2024
1 parent 706a038 commit 911db95
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/events/CustomItemModelEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ data class CustomItemModelEvent(
}

fun overrideIfExists(overrideModel: Identifier) {
TODO()
this.overrideModel = overrideModel
}
}
13 changes: 13 additions & 0 deletions src/texturePacks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!--
SPDX-FileCopyrightText: 2023 Linnea Gräf <[email protected]>
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
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,46 @@
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);
}
}
Original file line number Diff line number Diff line change
@@ -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);

}

}

0 comments on commit 911db95

Please sign in to comment.