@file:UseSerializers(IdentifierSerializer::class) package moe.nea.firmament.features.texturepack import java.util.Optional import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlinx.serialization.UseSerializers import kotlin.jvm.optionals.getOrNull import net.minecraft.item.ArmorMaterial import net.minecraft.item.ItemStack import net.minecraft.resource.ResourceManager import net.minecraft.resource.SinglePreparationResourceReloader import net.minecraft.util.Identifier import net.minecraft.util.profiler.Profiler import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.FinalizeResourceManagerEvent import moe.nea.firmament.events.subscription.SubscriptionOwner import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.collections.WeakCache import moe.nea.firmament.util.skyBlockId object CustomGlobalArmorOverrides : SubscriptionOwner { @Serializable data class ArmorOverride( @SerialName("item_ids") val itemIds: List, val layers: List, val overrides: List = listOf(), ) { @Transient val bakedLayers = bakeLayers(layers) } fun bakeLayers(layers: List): List { return layers.map { ArmorMaterial.Layer(it.identifier, it.suffix, it.tint) } } @Serializable data class ArmorOverrideLayer( val tint: Boolean = false, val identifier: Identifier, val suffix: String = "", ) @Serializable data class ArmorOverrideOverride( val predicate: FirmamentModelPredicate, val layers: List, ) { @Transient val bakedLayers = bakeLayers(layers) } override val delegateFeature: FirmamentFeature get() = CustomSkyBlockTextures val overrideCache = WeakCache.memoize>>("ArmorOverrides") { stack -> val id = stack.skyBlockId ?: return@memoize Optional.empty() val override = overrides[id.neuItem] ?: return@memoize Optional.empty() for (suboverride in override.overrides) { if (suboverride.predicate.test(stack)) { return@memoize Optional.of(suboverride.bakedLayers) } } return@memoize Optional.of(override.bakedLayers) } @JvmStatic fun overrideArmor(stack: ItemStack): List? { if (!CustomSkyBlockTextures.TConfig.enableArmorOverrides) return null return overrideCache.invoke(stack).getOrNull() } var overrides: Map = mapOf() @Subscribe fun onStart(event: FinalizeResourceManagerEvent) { event.resourceManager.registerReloader(object : SinglePreparationResourceReloader>() { override fun prepare(manager: ResourceManager, profiler: Profiler): Map { val overrideFiles = manager.findResources("overrides/armor_models") { it.namespace == "firmskyblock" && it.path.endsWith(".json") } val overrides = overrideFiles.mapNotNull { Firmament.tryDecodeJsonFromStream(it.value.inputStream).getOrElse { ex -> logger.error("Failed to load armor texture override at ${it.key}", ex) null } } val associatedMap = overrides.flatMap { obj -> obj.itemIds.map { it to obj } } .toMap() return associatedMap } override fun apply(prepared: Map, manager: ResourceManager, profiler: Profiler) { overrides = prepared } }) } }