fix: Fix (some) parts of custom block rendering.

This commit is contained in:
Linnea Gräf
2025-04-09 16:41:58 +02:00
parent 0abfc0068c
commit 72044baeff
8 changed files with 332 additions and 257 deletions

View File

@@ -7,6 +7,7 @@
*/ */
import com.google.common.hash.Hashing import com.google.common.hash.Hashing
import com.google.devtools.ksp.gradle.KspAATask
import com.google.devtools.ksp.gradle.KspTaskJvm import com.google.devtools.ksp.gradle.KspTaskJvm
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonObject import com.google.gson.JsonObject
@@ -32,7 +33,7 @@ plugins {
id("fabric-loom") version "1.10.1" id("fabric-loom") version "1.10.1"
alias(libs.plugins.shadow) alias(libs.plugins.shadow)
id("moe.nea.licenseextractificator") id("moe.nea.licenseextractificator")
id("moe.nea.mc-auto-translations") version "0.2.0" alias(libs.plugins.mcAutoTranslations)
} }
version = getGitTagInfo(libs.versions.minecraft.get()) version = getGitTagInfo(libs.versions.minecraft.get())
@@ -139,8 +140,10 @@ fun createIsolatedSourceSet(name: String, path: String = "compat/$name", isEnabl
val mainSS = sourceSets.main.get() val mainSS = sourceSets.main.get()
val upperName = ss.name.capitalizeN() val upperName = ss.name.capitalizeN()
afterEvaluate { afterEvaluate {
tasks.named("ksp${upperName}Kotlin", KspTaskJvm::class) { tasks.named("ksp${upperName}Kotlin", KspAATask::class) {
this.options.add(SubpluginOption("apoption", "firmament.sourceset=${ss.name}")) this.commandLineArgumentProviders.add { // TODO: update https://github.com/google/ksp/issues/2075
listOf("firmament.sourceset=${ss.name}")
}
} }
tasks.named("compile${upperName}Kotlin", KotlinCompile::class) { tasks.named("compile${upperName}Kotlin", KotlinCompile::class) {
this.enabled = isEnabled this.enabled = isEnabled
@@ -231,7 +234,7 @@ val jadeSourceSet = createIsolatedSourceSet("jade", isEnabled = false)
val modmenuSourceSet = createIsolatedSourceSet("modmenu") val modmenuSourceSet = createIsolatedSourceSet("modmenu")
val reiSourceSet = createIsolatedSourceSet("rei", isEnabled = false) val reiSourceSet = createIsolatedSourceSet("rei", isEnabled = false)
val moulconfigSourceSet = createIsolatedSourceSet("moulconfig") val moulconfigSourceSet = createIsolatedSourceSet("moulconfig")
val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks", isEnabled = false) val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks")
dependencies { dependencies {
// Minecraft dependencies // Minecraft dependencies

View File

@@ -6,20 +6,21 @@
minecraft = "1.21.5" minecraft = "1.21.5"
# Update from https://kotlinlang.org/ # Update from https://kotlinlang.org/
kotlin = "2.1.10" kotlin = "2.1.20"
# Update from https://github.com/google/ksp/releases # Update from https://github.com/google/ksp/releases
kotlin_ksp = "2.1.10-1.0.30" kotlin_ksp = "2.1.20-2.0.0"
# Update from https://linkie.shedaniel.me/dependencies?loader=fabric # Update from https://linkie.shedaniel.me/dependencies?loader=fabric
fabric_loader = "0.16.10" fabric_loader = "0.16.13"
fabric_api = "0.119.5+1.21.5" fabric_api = "0.119.9+1.21.5"
yarn = "1.21.5+build.1" yarn = "1.21.5+build.1"
modmenu = "14.0.0-rc.2" modmenu = "14.0.0-rc.2"
architectury = "16.0.3" architectury = "16.0.3"
# Update from https://maven.architectury.dev/me/shedaniel/RoughlyEnoughItems-fabric/ (but is typically late)
rei = "18.0.796" rei = "18.0.796"
# Update from https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/ # Update from https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/
fabric_kotlin = "1.13.1+kotlin.2.1.10" fabric_kotlin = "1.13.2+kotlin.2.1.20"
# Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/ # Update from https://maven.architectury.dev/dev/architectury/loom/dev.architectury.loom.gradle.plugin/
loom = "1.7.414" # TODO: port back to architectury (and) 1.9.424 loom = "1.7.414" # TODO: port back to architectury (and) 1.9.424
@@ -28,10 +29,10 @@ loom = "1.7.414" # TODO: port back to architectury (and) 1.9.424
qolify = "1.6.0-1.21.1" qolify = "1.6.0-1.21.1"
# Update from https://modrinth.com/mod/sodium/versions?l=fabric # Update from https://modrinth.com/mod/sodium/versions?l=fabric
sodium = "mc1.21.5-0.6.11-fabric" sodium = "mc1.21.5-0.6.13-fabric"
# Update from https://modrinth.com/mod/freecam/versions?l=fabric # Update from https://modrinth.com/mod/freecam/versions?l=fabric
freecammod = "1.3.2+mc1.21.4" freecammod = "1.3.3+mc1.21.5"
# Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric # Update from https://modrinth.com/mod/no-chat-reports/versions?l=fabric
ncr = "Fabric-1.21.5-v2.12.0" ncr = "Fabric-1.21.5-v2.12.0"
@@ -43,18 +44,18 @@ femalegender = "4.3.3+1.21.4"
explosiveenhancement = "1.2.3-1.21.0" explosiveenhancement = "1.2.3-1.21.0"
# Update from https://modrinth.com/mod/not-enough-animations/versions?l=fabric # Update from https://modrinth.com/mod/not-enough-animations/versions?l=fabric
notenoughanimations = "eZykTicT" notenoughanimations = "prj4BdjU"
# Update from https://modrinth.com/mod/cit-resewn/versions?l=fabric # Update from https://modrinth.com/mod/cit-resewn/versions?l=fabric
citresewn = "1.2.0+1.21" citresewn = "1.2.0+1.21"
# Update from https://modrinth.com/mod/jade/versions?l=fabric # Update from https://modrinth.com/mod/jade/versions?l=fabric
jade = "17.2.2+fabric" jade = "18.1.0+fabric"
devauth = "1.2.1" devauth = "1.2.1"
# Update from https://ktor.io/ # Update from https://ktor.io/docs/
ktor = "3.0.3" ktor = "3.1.2"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser # Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
neurepoparser = "1.7.0" neurepoparser = "1.7.0"
@@ -72,10 +73,13 @@ nealisp = "1.1.0"
# Update from https://github.com/NotEnoughUpdates/MoulConfig/tags # Update from https://github.com/NotEnoughUpdates/MoulConfig/tags
moulconfig = "3.3.0" moulconfig = "3.3.0"
# Update from https://repo.nea.moe/#/releases/moe/nea/mc-auto-translations/moe.nea.mc-auto-translations.gradle.plugin
mcAutoTranslations = "0.3.0"
# Update from https://www.curseforge.com/minecraft/mc-mods/configured/files/all?page=1&pageSize=20 # Update from https://www.curseforge.com/minecraft/mc-mods/configured/files/all?page=1&pageSize=20
configured = "6023970" configured = "6023970"
# Update from https://modrinth.com/mod/hypixel-mod-api/versions # Update from https://modrinth.com/mod/hypixel-mod-api/versions?l=fabric
hypixelmodapi = "1.0.1" hypixelmodapi = "1.0.1"
hypixelmodapi_fabric = "1.0.1+build.1+mc1.21" hypixelmodapi_fabric = "1.0.1+build.1+mc1.21"
@@ -86,14 +90,14 @@ manninghamMills = "2.4.1"
# Nvm, they just don't update docs: https://modrinth.com/mod/yacl/versions?l=fabric # Nvm, they just don't update docs: https://modrinth.com/mod/yacl/versions?l=fabric
yacl = "3.6.6+1.21.5-fabric" yacl = "3.6.6+1.21.5-fabric"
# Update from https://maven.shedaniel.me/me/shedaniel/cloth/basic-math/0.6.1/ # Update from https://maven.shedaniel.me/me/shedaniel/cloth/basic-math/
basicMath = "0.6.1" basicMath = "0.6.1"
# Update from https://mvnrepository.com/artifact/net.lenni0451.classtransform/core # Update from https://mvnrepository.com/artifact/net.lenni0451.classtransform/core
classtransform = "1.14.0" classtransform = "1.14.1"
# Update from https://mvnrepository.com/artifact/org.ow2.asm/asm/ # Update from https://mvnrepository.com/artifact/org.ow2.asm/asm/
asm = "9.7.1" asm = "9.8"
[libraries] [libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
@@ -159,3 +163,4 @@ kotlin_plugin_powerassert = { id = "org.jetbrains.kotlin.plugin.power-assert", v
kotlin_plugin_ksp = { id = "com.google.devtools.ksp", version.ref = "kotlin_ksp" } kotlin_plugin_ksp = { id = "com.google.devtools.ksp", version.ref = "kotlin_ksp" }
loom = { id = "dev.architectury.loom", version.ref = "loom" } loom = { id = "dev.architectury.loom", version.ref = "loom" }
shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" }
mcAutoTranslations = { id = "moe.nea.mc-auto-translations", version.ref = "mcAutoTranslations" }

View File

@@ -6,6 +6,8 @@ accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARA
accessible field net/minecraft/client/network/ClientPlayNetworkHandler combinedDynamicRegistries Lnet/minecraft/registry/DynamicRegistryManager$Immutable; accessible field net/minecraft/client/network/ClientPlayNetworkHandler combinedDynamicRegistries Lnet/minecraft/registry/DynamicRegistryManager$Immutable;
accessible method net/minecraft/registry/RegistryOps <init> (Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/registry/RegistryOps$RegistryInfoGetter;)V accessible method net/minecraft/registry/RegistryOps <init> (Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/registry/RegistryOps$RegistryInfoGetter;)V
accessible class net/minecraft/registry/RegistryOps$CachedRegistryInfoGetter accessible class net/minecraft/registry/RegistryOps$CachedRegistryInfoGetter
accessible class net/minecraft/client/render/model/ModelBaker$BakerImpl
accessible method net/minecraft/client/render/model/ModelBaker$BakerImpl <init> (Lnet/minecraft/client/render/model/ModelBaker;Lnet/minecraft/client/render/model/ErrorCollectingSpriteGetter;)V
accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData; accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData;
accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V accessible method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V

View File

@@ -3,6 +3,8 @@
package moe.nea.firmament.features.texturepack package moe.nea.firmament.features.texturepack
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.function.Function
import net.fabricmc.loader.api.FabricLoader import net.fabricmc.loader.api.FabricLoader
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -19,8 +21,10 @@ import kotlinx.serialization.serializer
import kotlin.jvm.optionals.getOrNull import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.BlockState import net.minecraft.block.BlockState
import net.minecraft.client.render.item.model.ItemModel import net.minecraft.client.render.model.Baker
import net.minecraft.client.render.model.BlockStateModel import net.minecraft.client.render.model.BlockStateModel
import net.minecraft.client.render.model.SimpleBlockStateModel
import net.minecraft.client.render.model.json.ModelVariant
import net.minecraft.registry.RegistryKey import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys import net.minecraft.registry.RegistryKeys
import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceManager
@@ -28,11 +32,13 @@ import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import net.minecraft.util.profiler.Profiler import net.minecraft.util.profiler.Profiler
import net.minecraft.util.thread.AsyncHelper
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.EarlyResourceReloadEvent import moe.nea.firmament.events.EarlyResourceReloadEvent
import moe.nea.firmament.events.FinalizeResourceManagerEvent import moe.nea.firmament.events.FinalizeResourceManagerEvent
import moe.nea.firmament.events.SkyblockServerUpdateEvent import moe.nea.firmament.events.SkyblockServerUpdateEvent
import moe.nea.firmament.features.texturepack.CustomBlockTextures.createBakedModels
import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
import moe.nea.firmament.util.IdentifierSerializer import moe.nea.firmament.util.IdentifierSerializer
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
@@ -43,244 +49,279 @@ import moe.nea.firmament.util.json.SingletonSerializableList
object CustomBlockTextures { object CustomBlockTextures {
@Serializable @Serializable
data class CustomBlockOverride( data class CustomBlockOverride(
val modes: @Serializable(SingletonSerializableList::class) List<String>, val modes: @Serializable(SingletonSerializableList::class) List<String>,
val area: List<Area>? = null, val area: List<Area>? = null,
val replacements: Map<Identifier, Replacement>, val replacements: Map<Identifier, Replacement>,
) )
@Serializable(with = Replacement.Serializer::class) @Serializable(with = Replacement.Serializer::class)
data class Replacement( data class Replacement(
val block: Identifier, val block: Identifier,
val sound: Identifier?, val sound: Identifier?,
) { ) {
@Transient @Transient
val blockModelIdentifier get() = block.withPrefixedPath("block/") val blockModelIdentifier get() = block.withPrefixedPath("block/")
@Transient /**
val bakedModel: ItemModel by lazy(LazyThreadSafetyMode.NONE) { * Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete.
MC.instance.bakedModelManager.blockModels.(blockModelIdentifier) */
} @Transient
lateinit var blockModel: BlockStateModel
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
@kotlinx.serialization.Serializer(Replacement::class) @kotlinx.serialization.Serializer(Replacement::class)
object DefaultSerializer : KSerializer<Replacement> object DefaultSerializer : KSerializer<Replacement>
object Serializer : KSerializer<Replacement> { object Serializer : KSerializer<Replacement> {
val delegate = serializer<JsonElement>() val delegate = serializer<JsonElement>()
override val descriptor: SerialDescriptor override val descriptor: SerialDescriptor
get() = delegate.descriptor get() = delegate.descriptor
override fun deserialize(decoder: Decoder): Replacement { override fun deserialize(decoder: Decoder): Replacement {
val jsonElement = decoder.decodeSerializableValue(delegate) val jsonElement = decoder.decodeSerializableValue(delegate)
if (jsonElement is JsonPrimitive) { if (jsonElement is JsonPrimitive) {
require(jsonElement.isString) require(jsonElement.isString)
return Replacement(Identifier.tryParse(jsonElement.content)!!, null) return Replacement(Identifier.tryParse(jsonElement.content)!!, null)
} }
return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement) return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement)
} }
override fun serialize(encoder: Encoder, value: Replacement) { override fun serialize(encoder: Encoder, value: Replacement) {
encoder.encodeSerializableValue(DefaultSerializer, value) encoder.encodeSerializableValue(DefaultSerializer, value)
} }
} }
} }
@Serializable @Serializable
data class Area( data class Area(
val min: BlockPos, val min: BlockPos,
val max: BlockPos, val max: BlockPos,
) { ) {
@Transient @Transient
val realMin = BlockPos( val realMin = BlockPos(
minOf(min.x, max.x), minOf(min.x, max.x),
minOf(min.y, max.y), minOf(min.y, max.y),
minOf(min.z, max.z), minOf(min.z, max.z),
) )
@Transient @Transient
val realMax = BlockPos( val realMax = BlockPos(
maxOf(min.x, max.x), maxOf(min.x, max.x),
maxOf(min.y, max.y), maxOf(min.y, max.y),
maxOf(min.z, max.z), maxOf(min.z, max.z),
) )
fun roughJoin(other: Area): Area { fun roughJoin(other: Area): Area {
return Area( return Area(
BlockPos( BlockPos(
minOf(realMin.x, other.realMin.x), minOf(realMin.x, other.realMin.x),
minOf(realMin.y, other.realMin.y), minOf(realMin.y, other.realMin.y),
minOf(realMin.z, other.realMin.z), minOf(realMin.z, other.realMin.z),
), ),
BlockPos( BlockPos(
maxOf(realMax.x, other.realMax.x), maxOf(realMax.x, other.realMax.x),
maxOf(realMax.y, other.realMax.y), maxOf(realMax.y, other.realMax.y),
maxOf(realMax.z, other.realMax.z), maxOf(realMax.z, other.realMax.z),
) )
) )
} }
fun contains(blockPos: BlockPos): Boolean { fun contains(blockPos: BlockPos): Boolean {
return (blockPos.x in realMin.x..realMax.x) && return (blockPos.x in realMin.x..realMax.x) &&
(blockPos.y in realMin.y..realMax.y) && (blockPos.y in realMin.y..realMax.y) &&
(blockPos.z in realMin.z..realMax.z) (blockPos.z in realMin.z..realMax.z)
} }
} }
data class LocationReplacements( data class LocationReplacements(
val lookup: Map<Block, List<BlockReplacement>> val lookup: Map<Block, List<BlockReplacement>>
) )
data class BlockReplacement( data class BlockReplacement(
val checks: List<Area>?, val checks: List<Area>?,
val replacement: Replacement, val replacement: Replacement,
) { ) {
val roughCheck by lazy(LazyThreadSafetyMode.NONE) { val roughCheck by lazy(LazyThreadSafetyMode.NONE) {
if (checks == null || checks.size < 3) return@lazy null if (checks == null || checks.size < 3) return@lazy null
checks.reduce { acc, next -> acc.roughJoin(next) } checks.reduce { acc, next -> acc.roughJoin(next) }
} }
} }
data class BakedReplacements(val data: Map<SkyBlockIsland, LocationReplacements>) data class BakedReplacements(val data: Map<SkyBlockIsland, LocationReplacements>) {
/**
* Fulfilled by [createBakedModels] which is called during model baking. Once completed, all [Replacement.blockModel] will be set.
*/
val modelBakingFuture = CompletableFuture<Unit>()
var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf()) /**
var currentIslandReplacements: LocationReplacements? = null * @returns a list of all [Replacement]s.
*/
fun collectAllReplacements(): Sequence<Replacement> {
return data.values.asSequence()
.flatMap { it.lookup.values }
.flatten()
.map { it.replacement }
}
}
fun refreshReplacements() { var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf())
val location = SBData.skyblockLocation var currentIslandReplacements: LocationReplacements? = null
val replacements =
if (CustomSkyBlockTextures.TConfig.enableBlockOverrides) location?.let(allLocationReplacements.data::get)
else null
val lastReplacements = currentIslandReplacements
currentIslandReplacements = replacements
if (lastReplacements != replacements) {
MC.nextTick {
MC.worldRenderer.chunks?.chunks?.forEach {
// false schedules rebuilds outside a 27 block radius to happen async
it.scheduleRebuild(false)
}
sodiumReloadTask?.run()
}
}
}
private val sodiumReloadTask = runCatching { fun refreshReplacements() {
val r = Class.forName("moe.nea.firmament.compat.sodium.SodiumChunkReloader") val location = SBData.skyblockLocation
.getConstructor() val replacements =
.newInstance() as Runnable if (CustomSkyBlockTextures.TConfig.enableBlockOverrides) location?.let(allLocationReplacements.data::get)
r.run() else null
r val lastReplacements = currentIslandReplacements
}.getOrElse { currentIslandReplacements = replacements
if (FabricLoader.getInstance().isModLoaded("sodium")) if (lastReplacements != replacements) {
logger.error("Could not create sodium chunk reloader") MC.nextTick {
null MC.worldRenderer.chunks?.chunks?.forEach {
} // false schedules rebuilds outside a 27 block radius to happen async
it.scheduleRebuild(false)
}
sodiumReloadTask?.run()
}
}
}
private val sodiumReloadTask = runCatching {
val r = Class.forName("moe.nea.firmament.compat.sodium.SodiumChunkReloader")
.getConstructor()
.newInstance() as Runnable
r.run()
r
}.getOrElse {
if (FabricLoader.getInstance().isModLoaded("sodium"))
logger.error("Could not create sodium chunk reloader")
null
}
fun matchesPosition(replacement: BlockReplacement, blockPos: BlockPos?): Boolean { fun matchesPosition(replacement: BlockReplacement, blockPos: BlockPos?): Boolean {
if (blockPos == null) return true if (blockPos == null) return true
val rc = replacement.roughCheck val rc = replacement.roughCheck
if (rc != null && !rc.contains(blockPos)) return false if (rc != null && !rc.contains(blockPos)) return false
val areas = replacement.checks val areas = replacement.checks
if (areas != null && !areas.any { it.contains(blockPos) }) return false if (areas != null && !areas.any { it.contains(blockPos) }) return false
return true return true
} }
@JvmStatic @JvmStatic
fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? { fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? {
return getReplacement(block, blockPos)?.bakedModel return getReplacement(block, blockPos)?.blockModel
} }
@JvmStatic @JvmStatic
fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? { fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? {
if (isInFallback() && blockPos == null) { if (isInFallback() && blockPos == null) {
return null return null
} }
val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null
for (replacement in replacements) { for (replacement in replacements) {
if (replacement.checks == null || matchesPosition(replacement, blockPos)) if (replacement.checks == null || matchesPosition(replacement, blockPos))
return replacement.replacement return replacement.replacement
} }
return null return null
} }
@Subscribe @Subscribe
fun onLocation(event: SkyblockServerUpdateEvent) { fun onLocation(event: SkyblockServerUpdateEvent) {
refreshReplacements() refreshReplacements()
} }
@Volatile @Volatile
var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements( var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements(
mapOf())) mapOf()))
val insideFallbackCall = ThreadLocal.withInitial { 0 } val insideFallbackCall = ThreadLocal.withInitial { 0 }
@JvmStatic @JvmStatic
fun enterFallbackCall() { fun enterFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() + 1) insideFallbackCall.set(insideFallbackCall.get() + 1)
} }
fun isInFallback() = insideFallbackCall.get() > 0 fun isInFallback() = insideFallbackCall.get() > 0
@JvmStatic @JvmStatic
fun exitFallbackCall() { fun exitFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() - 1) insideFallbackCall.set(insideFallbackCall.get() - 1)
} }
@Subscribe @Subscribe
fun onEarlyReload(event: EarlyResourceReloadEvent) { fun onEarlyReload(event: EarlyResourceReloadEvent) {
preparationFuture = CompletableFuture preparationFuture = CompletableFuture
.supplyAsync( .supplyAsync(
{ prepare(event.resourceManager) }, event.preparationExecutor) { prepare(event.resourceManager) }, event.preparationExecutor)
} }
private fun prepare(manager: ResourceManager): BakedReplacements { private fun prepare(manager: ResourceManager): BakedReplacements {
val resources = manager.findResources("overrides/blocks") { val resources = manager.findResources("overrides/blocks") {
it.namespace == "firmskyblock" && it.path.endsWith(".json") it.namespace == "firmskyblock" && it.path.endsWith(".json")
} }
val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>() val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>()
for ((file, resource) in resources) { for ((file, resource) in resources) {
val json = val json =
Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream) Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream)
.getOrElse { ex -> .getOrElse { ex ->
logger.error("Failed to load block texture override at $file", ex) logger.error("Failed to load block texture override at $file", ex)
continue continue
} }
for (mode in json.modes) { for (mode in json.modes) {
val island = SkyBlockIsland.forMode(mode) val island = SkyBlockIsland.forMode(mode)
val islandMpa = map.getOrPut(island, ::mutableMapOf) val islandMpa = map.getOrPut(island, ::mutableMapOf)
for ((blockId, replacement) in json.replacements) { for ((blockId, replacement) in json.replacements) {
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK) val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId)) .getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
.getOrNull() .getOrNull()
if (block == null) { if (block == null) {
logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'") logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'")
continue continue
} }
val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf) val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf)
replacements.add(BlockReplacement(json.area, replacement)) replacements.add(BlockReplacement(json.area, replacement))
} }
} }
} }
return BakedReplacements(map.mapValues { LocationReplacements(it.value) }) return BakedReplacements(map.mapValues { LocationReplacements(it.value) })
} }
@Subscribe @Subscribe
fun onStart(event: FinalizeResourceManagerEvent) { fun onStart(event: FinalizeResourceManagerEvent) {
event.resourceManager.registerReloader(object : event.resourceManager.registerReloader(object :
SinglePreparationResourceReloader<BakedReplacements>() { SinglePreparationResourceReloader<BakedReplacements>() {
override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements { override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements {
return preparationFuture.join() return preparationFuture.join().also {
} it.modelBakingFuture.join()
}
}
override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) { override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) {
allLocationReplacements = prepared allLocationReplacements = prepared
refreshReplacements() refreshReplacements()
} }
}) })
} }
@JvmStatic
fun createBakedModels(baker: Baker, executor: Executor): CompletableFuture<Void?> {
return preparationFuture.thenComposeAsync(Function { replacements ->
val byModel = replacements.collectAllReplacements().groupBy { it.blockModelIdentifier }
val modelBakingTask = AsyncHelper.mapValues(byModel, { blockId, replacements ->
val unbakedModel = SimpleBlockStateModel.Unbaked(
ModelVariant(blockId)
) // TODO: do i need to resolve models here? Maybe this needs to be resolved earlier before baking.
val baked = unbakedModel.bake(baker)
replacements.forEach {
it.blockModel = baked
}
}, executor)
modelBakingTask.thenAcceptAsync { replacements.modelBakingFuture.complete(Unit) }
}, executor)
}
} }

View File

@@ -0,0 +1,24 @@
package moe.nea.firmament.mixins.custommodels;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.features.texturepack.CustomBlockTextures;
import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBaker;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@Mixin(ModelBaker.class)
public class BuildExtraBlockStateModels {
@ModifyReturnValue(method = "bake", at = @At("RETURN"))
private CompletableFuture<ModelBaker.BakedModels> injectMoreBlockModels(CompletableFuture<ModelBaker.BakedModels> original, @Local ModelBaker.BakerImpl baker, @Local(argsOnly = true) Executor executor) {
Baker b = baker;
return original.thenCombine(
CustomBlockTextures.createBakedModels(b, executor),
(a, _void) -> a
);
}
}

View File

@@ -5,10 +5,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import moe.nea.firmament.features.texturepack.CustomBlockTextures; import moe.nea.firmament.features.texturepack.CustomBlockTextures;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.block.BlockRenderManager; import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.chunk.SectionBuilder; import net.minecraft.client.render.chunk.SectionBuilder;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BlockStateModel; import net.minecraft.client.render.model.BlockStateModel;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -16,24 +14,24 @@ import org.spongepowered.asm.mixin.injection.At;
@Mixin(SectionBuilder.class) @Mixin(SectionBuilder.class)
public class ReplaceBlockRenderManagerBlockModel { public class ReplaceBlockRenderManagerBlockModel {
@WrapOperation(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockRenderManager;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BlockStateModel;")) @WrapOperation(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockRenderManager;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BlockStateModel;"))
private BlockStateModel replaceModelInRenderBlock(BlockRenderManager instance, BlockState state, Operation<BlockStateModel> original, @Local(argsOnly = true) BlockPos pos) { private BlockStateModel replaceModelInRenderBlock(BlockRenderManager instance, BlockState state, Operation<BlockStateModel> original, @Local(ordinal = 2) BlockPos pos) {
var replacement = CustomBlockTextures.getReplacementModel(state, pos); var replacement = CustomBlockTextures.getReplacementModel(state, pos);
if (replacement != null) return replacement; if (replacement != null) return replacement;
CustomBlockTextures.enterFallbackCall(); CustomBlockTextures.enterFallbackCall();
var fallback = original.call(instance, state); var fallback = original.call(instance, state);
CustomBlockTextures.exitFallbackCall(); CustomBlockTextures.exitFallbackCall();
return fallback; return fallback;
} }
//TODO: cover renderDamage model
@WrapOperation(method = "renderDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;")) // @WrapOperation(method = "renderDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;"))
private BakedModel replaceModelInRenderDamage( // private BakedModel replaceModelInRenderDamage(
BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) { // BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) {
var replacement = CustomBlockTextures.getReplacementModel(state, pos); // var replacement = CustomBlockTextures.getReplacementModel(state, pos);
if (replacement != null) return replacement; // if (replacement != null) return replacement;
CustomBlockTextures.enterFallbackCall(); // CustomBlockTextures.enterFallbackCall();
var fallback = original.call(instance, state); // var fallback = original.call(instance, state);
CustomBlockTextures.exitFallbackCall(); // CustomBlockTextures.exitFallbackCall();
return fallback; // return fallback;
} // }
} }

View File

@@ -3,7 +3,7 @@ package moe.nea.firmament.mixins.custommodels;
import moe.nea.firmament.features.texturepack.CustomBlockTextures; import moe.nea.firmament.features.texturepack.CustomBlockTextures;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModels; import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BlockStateModel;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class ReplaceFallbackBlockModel { public class ReplaceFallbackBlockModel {
// TODO: add check to BlockDustParticle // TODO: add check to BlockDustParticle
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true) @Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void getModel(BlockState state, CallbackInfoReturnable<BakedModel> cir) { private void getModel(BlockState state, CallbackInfoReturnable<BlockStateModel> cir) {
var replacement = CustomBlockTextures.getReplacementModel(state, null); var replacement = CustomBlockTextures.getReplacementModel(state, null);
if (replacement != null) if (replacement != null)
cir.setReturnValue(replacement); cir.setReturnValue(replacement);

View File

@@ -25,7 +25,7 @@ class SubscribeAnnotationProcessor(
override fun finish() { override fun finish() {
subscriptions.sort() subscriptions.sort()
if (subscriptions.isEmpty()) return if (subscriptions.isEmpty()) return
val subscriptionSet = subscriptions.mapTo(mutableSetOf()) { it.parent.containingFile!! } val subscriptionSet = subscriptions.mapTo(mutableSetOf()) { it.cf }
val dependencies = Dependencies( val dependencies = Dependencies(
aggregating = true, aggregating = true,
*subscriptionSet.toTypedArray()) *subscriptionSet.toTypedArray())
@@ -38,7 +38,7 @@ class SubscribeAnnotationProcessor(
appendLine("// This file is @generated by SubscribeAnnotationProcessor") appendLine("// This file is @generated by SubscribeAnnotationProcessor")
appendLine("// Do not edit") appendLine("// Do not edit")
for (file in subscriptionSet) { for (file in subscriptionSet) {
appendLine("// Dependency: ${file.filePath}") appendLine("// Dependency: ${"TODO"?:file.filePath}")
} }
appendLine("package moe.nea.firmament.annotations.generated.$sourceSetName") appendLine("package moe.nea.firmament.annotations.generated.$sourceSetName")
appendLine() appendLine()
@@ -48,7 +48,7 @@ class SubscribeAnnotationProcessor(
appendLine("class $generatedFileName : SubscriptionList {") appendLine("class $generatedFileName : SubscriptionList {")
appendLine(" override fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {") appendLine(" override fun provideSubscriptions(addSubscription: (Subscription<*>) -> Unit) {")
for (subscription in subscriptions) { for (subscription in subscriptions) {
val owner = subscription.parent.qualifiedName!!.asString() val owner = subscription.pQName.asString()
val method = subscription.child.simpleName.asString() val method = subscription.child.simpleName.asString()
val type = subscription.type.declaration.qualifiedName!!.asString() val type = subscription.type.declaration.qualifiedName!!.asString()
appendLine(" addSubscription(Subscription<$type>(") appendLine(" addSubscription(Subscription<$type>(")
@@ -75,13 +75,15 @@ class SubscribeAnnotationProcessor(
val child: KSFunctionDeclaration, val child: KSFunctionDeclaration,
val type: KSType, val type: KSType,
) : Comparable<Subscription> { ) : Comparable<Subscription> {
val cf = parent.containingFile!!
val pQName = parent.qualifiedName!!
val tName = type.declaration.qualifiedName!!
override fun compareTo(other: Subscription): Int { override fun compareTo(other: Subscription): Int {
var compare = parent.qualifiedName!!.asString().compareTo(other.parent.qualifiedName!!.asString()) var compare = pQName.asString().compareTo(other.pQName.asString())
if (compare != 0) return compare if (compare != 0) return compare
compare = other.child.simpleName.asString().compareTo(child.simpleName.asString()) compare = other.child.simpleName.asString().compareTo(child.simpleName.asString())
if (compare != 0) return compare if (compare != 0) return compare
compare = other.type.declaration.qualifiedName!!.asString() compare = other.tName.asString().compareTo(tName.asString())
.compareTo(type.declaration.qualifiedName!!.asString())
if (compare != 0) return compare if (compare != 0) return compare
return 0 return 0
} }