diff options
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt')
-rw-r--r-- | src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt new file mode 100644 index 0000000..2eb4ee1 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomGlobalTextures.kt @@ -0,0 +1,161 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe> + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +@file:UseSerializers(IdentifierSerializer::class, CustomModelOverrideParser.FirmamentRootPredicateSerializer::class) + +package moe.nea.firmament.features.texturepack + + +import java.util.concurrent.CompletableFuture +import org.slf4j.LoggerFactory +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlin.jvm.optionals.getOrNull +import net.minecraft.client.render.item.ItemModels +import net.minecraft.client.render.model.BakedModel +import net.minecraft.client.util.ModelIdentifier +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.BakeExtraModelsEvent +import moe.nea.firmament.events.EarlyResourceReloadEvent +import moe.nea.firmament.events.FinalizeResourceManagerEvent +import moe.nea.firmament.events.ScreenChangeEvent +import moe.nea.firmament.events.subscription.SubscriptionOwner +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.util.IdentifierSerializer +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.runNull + +object CustomGlobalTextures : SinglePreparationResourceReloader<CustomGlobalTextures.CustomGuiTextureOverride>(), + SubscriptionOwner { + override val delegateFeature: FirmamentFeature + get() = CustomSkyBlockTextures + + class CustomGuiTextureOverride( + val classes: List<ItemOverrideCollection> + ) + + @Serializable + data class GlobalItemOverride( + val screen: Identifier, + val model: Identifier, + val predicate: FirmamentModelPredicate, + ) + + @Serializable + data class ScreenFilter( + val title: StringMatcher, + ) + + data class ItemOverrideCollection( + val screenFilter: ScreenFilter, + val overrides: List<GlobalItemOverride>, + ) + + @Subscribe + fun onStart(event: FinalizeResourceManagerEvent) { + MC.resourceManager.registerReloader(this) + } + + @Subscribe + fun onEarlyReload(event: EarlyResourceReloadEvent) { + preparationFuture = CompletableFuture + .supplyAsync( + { + prepare(event.resourceManager) + }, event.preparationExecutor) + } + + @Subscribe + fun onBakeModels(event: BakeExtraModelsEvent) { + for (guiClassOverride in preparationFuture.join().classes) { + for (override in guiClassOverride.overrides) { + event.addModel(ModelIdentifier(override.model, "inventory")) + } + } + } + + @Volatile + var preparationFuture: CompletableFuture<CustomGuiTextureOverride> = CompletableFuture.completedFuture( + CustomGuiTextureOverride(listOf())) + + override fun prepare(manager: ResourceManager?, profiler: Profiler?): CustomGuiTextureOverride { + return preparationFuture.join() + } + + override fun apply(prepared: CustomGuiTextureOverride, manager: ResourceManager?, profiler: Profiler?) { + this.guiClassOverrides = prepared + } + + val logger = LoggerFactory.getLogger(CustomGlobalTextures::class.java) + fun prepare(manager: ResourceManager): CustomGuiTextureOverride { + val overrideResources = + manager.findResources("overrides/item") { it.namespace == "firmskyblock" && it.path.endsWith(".json") } + .mapNotNull { + Firmament.tryDecodeJsonFromStream<GlobalItemOverride>(it.value.inputStream).getOrElse { ex -> + logger.error("Failed to load global item override at ${it.key}", ex) + null + } + } + + val byGuiClass = overrideResources.groupBy { it.screen } + val guiClasses = byGuiClass.entries + .mapNotNull { + val key = it.key + val guiClassResource = + manager.getResource(Identifier(key.namespace, "filters/screen/${key.path}.json")) + .getOrNull() + ?: return@mapNotNull runNull { + logger.error("Failed to locate screen filter at $key") + } + val screenFilter = + Firmament.tryDecodeJsonFromStream<ScreenFilter>(guiClassResource.inputStream) + .getOrElse { ex -> + logger.error("Failed to load screen filter at $key", ex) + return@mapNotNull null + } + ItemOverrideCollection(screenFilter, it.value) + } + logger.info("Loaded ${overrideResources.size} global item overrides") + return CustomGuiTextureOverride(guiClasses) + } + + var guiClassOverrides = CustomGuiTextureOverride(listOf()) + + var matchingOverrides: List<ItemOverrideCollection> = listOf() + + @Subscribe + fun onOpenGui(event: ScreenChangeEvent) { + val newTitle = event.new?.title + matchingOverrides = + if (newTitle == null) listOf() + else guiClassOverrides.classes.filter { it.screenFilter.title.matches(newTitle) } + } + + @JvmStatic + fun replaceGlobalModel( + models: ItemModels, + stack: ItemStack, + cir: CallbackInfoReturnable<BakedModel> + ) { + for (guiClassOverride in matchingOverrides) { + for (override in guiClassOverride.overrides) { + if (override.predicate.test(stack)) { + cir.returnValue = models.modelManager.getModel(ModelIdentifier(override.model, "inventory")) + return + } + } + } + } + + +} |