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.devtools.ksp.gradle.KspAATask
import com.google.devtools.ksp.gradle.KspTaskJvm
import com.google.gson.Gson
import com.google.gson.JsonObject
@@ -32,7 +33,7 @@ plugins {
id("fabric-loom") version "1.10.1"
alias(libs.plugins.shadow)
id("moe.nea.licenseextractificator")
id("moe.nea.mc-auto-translations") version "0.2.0"
alias(libs.plugins.mcAutoTranslations)
}
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 upperName = ss.name.capitalizeN()
afterEvaluate {
tasks.named("ksp${upperName}Kotlin", KspTaskJvm::class) {
this.options.add(SubpluginOption("apoption", "firmament.sourceset=${ss.name}"))
tasks.named("ksp${upperName}Kotlin", KspAATask::class) {
this.commandLineArgumentProviders.add { // TODO: update https://github.com/google/ksp/issues/2075
listOf("firmament.sourceset=${ss.name}")
}
}
tasks.named("compile${upperName}Kotlin", KotlinCompile::class) {
this.enabled = isEnabled
@@ -231,7 +234,7 @@ val jadeSourceSet = createIsolatedSourceSet("jade", isEnabled = false)
val modmenuSourceSet = createIsolatedSourceSet("modmenu")
val reiSourceSet = createIsolatedSourceSet("rei", isEnabled = false)
val moulconfigSourceSet = createIsolatedSourceSet("moulconfig")
val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks", isEnabled = false)
val customTexturesSourceSet = createIsolatedSourceSet("texturePacks", "texturePacks")
dependencies {
// Minecraft dependencies

View File

@@ -6,20 +6,21 @@
minecraft = "1.21.5"
# Update from https://kotlinlang.org/
kotlin = "2.1.10"
kotlin = "2.1.20"
# 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
fabric_loader = "0.16.10"
fabric_api = "0.119.5+1.21.5"
fabric_loader = "0.16.13"
fabric_api = "0.119.9+1.21.5"
yarn = "1.21.5+build.1"
modmenu = "14.0.0-rc.2"
architectury = "16.0.3"
# Update from https://maven.architectury.dev/me/shedaniel/RoughlyEnoughItems-fabric/ (but is typically late)
rei = "18.0.796"
# 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/
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"
# 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
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
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"
# 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
citresewn = "1.2.0+1.21"
# Update from https://modrinth.com/mod/jade/versions?l=fabric
jade = "17.2.2+fabric"
jade = "18.1.0+fabric"
devauth = "1.2.1"
# Update from https://ktor.io/
ktor = "3.0.3"
# Update from https://ktor.io/docs/
ktor = "3.1.2"
# Update from https://repo.nea.moe/#/releases/moe/nea/neurepoparser
neurepoparser = "1.7.0"
@@ -72,10 +73,13 @@ nealisp = "1.1.0"
# Update from https://github.com/NotEnoughUpdates/MoulConfig/tags
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
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_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
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"
# 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/
asm = "9.7.1"
asm = "9.8"
[libraries]
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" }
loom = { id = "dev.architectury.loom", version.ref = "loom" }
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 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/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 method net/minecraft/entity/decoration/ArmorStandEntity setSmall (Z)V

View File

@@ -3,6 +3,8 @@
package moe.nea.firmament.features.texturepack
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.function.Function
import net.fabricmc.loader.api.FabricLoader
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
@@ -19,8 +21,10 @@ import kotlinx.serialization.serializer
import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.Block
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.SimpleBlockStateModel
import net.minecraft.client.render.model.json.ModelVariant
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.resource.ResourceManager
@@ -28,11 +32,13 @@ import net.minecraft.resource.SinglePreparationResourceReloader
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.profiler.Profiler
import net.minecraft.util.thread.AsyncHelper
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.EarlyResourceReloadEvent
import moe.nea.firmament.events.FinalizeResourceManagerEvent
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.util.IdentifierSerializer
import moe.nea.firmament.util.MC
@@ -43,244 +49,279 @@ import moe.nea.firmament.util.json.SingletonSerializableList
object CustomBlockTextures {
@Serializable
data class CustomBlockOverride(
val modes: @Serializable(SingletonSerializableList::class) List<String>,
val area: List<Area>? = null,
val replacements: Map<Identifier, Replacement>,
)
@Serializable
data class CustomBlockOverride(
val modes: @Serializable(SingletonSerializableList::class) List<String>,
val area: List<Area>? = null,
val replacements: Map<Identifier, Replacement>,
)
@Serializable(with = Replacement.Serializer::class)
data class Replacement(
val block: Identifier,
val sound: Identifier?,
) {
@Serializable(with = Replacement.Serializer::class)
data class Replacement(
val block: Identifier,
val sound: Identifier?,
) {
@Transient
val blockModelIdentifier get() = block.withPrefixedPath("block/")
@Transient
val blockModelIdentifier get() = block.withPrefixedPath("block/")
@Transient
val bakedModel: ItemModel by lazy(LazyThreadSafetyMode.NONE) {
MC.instance.bakedModelManager.blockModels.(blockModelIdentifier)
}
/**
* Guaranteed to be set after [BakedReplacements.modelBakingFuture] is complete.
*/
@Transient
lateinit var blockModel: BlockStateModel
@OptIn(ExperimentalSerializationApi::class)
@kotlinx.serialization.Serializer(Replacement::class)
object DefaultSerializer : KSerializer<Replacement>
@OptIn(ExperimentalSerializationApi::class)
@kotlinx.serialization.Serializer(Replacement::class)
object DefaultSerializer : KSerializer<Replacement>
object Serializer : KSerializer<Replacement> {
val delegate = serializer<JsonElement>()
override val descriptor: SerialDescriptor
get() = delegate.descriptor
object Serializer : KSerializer<Replacement> {
val delegate = serializer<JsonElement>()
override val descriptor: SerialDescriptor
get() = delegate.descriptor
override fun deserialize(decoder: Decoder): Replacement {
val jsonElement = decoder.decodeSerializableValue(delegate)
if (jsonElement is JsonPrimitive) {
require(jsonElement.isString)
return Replacement(Identifier.tryParse(jsonElement.content)!!, null)
}
return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement)
}
override fun deserialize(decoder: Decoder): Replacement {
val jsonElement = decoder.decodeSerializableValue(delegate)
if (jsonElement is JsonPrimitive) {
require(jsonElement.isString)
return Replacement(Identifier.tryParse(jsonElement.content)!!, null)
}
return (decoder as JsonDecoder).json.decodeFromJsonElement(DefaultSerializer, jsonElement)
}
override fun serialize(encoder: Encoder, value: Replacement) {
encoder.encodeSerializableValue(DefaultSerializer, value)
}
}
}
override fun serialize(encoder: Encoder, value: Replacement) {
encoder.encodeSerializableValue(DefaultSerializer, value)
}
}
}
@Serializable
data class Area(
val min: BlockPos,
val max: BlockPos,
) {
@Transient
val realMin = BlockPos(
minOf(min.x, max.x),
minOf(min.y, max.y),
minOf(min.z, max.z),
)
@Serializable
data class Area(
val min: BlockPos,
val max: BlockPos,
) {
@Transient
val realMin = BlockPos(
minOf(min.x, max.x),
minOf(min.y, max.y),
minOf(min.z, max.z),
)
@Transient
val realMax = BlockPos(
maxOf(min.x, max.x),
maxOf(min.y, max.y),
maxOf(min.z, max.z),
)
@Transient
val realMax = BlockPos(
maxOf(min.x, max.x),
maxOf(min.y, max.y),
maxOf(min.z, max.z),
)
fun roughJoin(other: Area): Area {
return Area(
BlockPos(
minOf(realMin.x, other.realMin.x),
minOf(realMin.y, other.realMin.y),
minOf(realMin.z, other.realMin.z),
),
BlockPos(
maxOf(realMax.x, other.realMax.x),
maxOf(realMax.y, other.realMax.y),
maxOf(realMax.z, other.realMax.z),
)
)
}
fun roughJoin(other: Area): Area {
return Area(
BlockPos(
minOf(realMin.x, other.realMin.x),
minOf(realMin.y, other.realMin.y),
minOf(realMin.z, other.realMin.z),
),
BlockPos(
maxOf(realMax.x, other.realMax.x),
maxOf(realMax.y, other.realMax.y),
maxOf(realMax.z, other.realMax.z),
)
)
}
fun contains(blockPos: BlockPos): Boolean {
return (blockPos.x in realMin.x..realMax.x) &&
(blockPos.y in realMin.y..realMax.y) &&
(blockPos.z in realMin.z..realMax.z)
}
}
fun contains(blockPos: BlockPos): Boolean {
return (blockPos.x in realMin.x..realMax.x) &&
(blockPos.y in realMin.y..realMax.y) &&
(blockPos.z in realMin.z..realMax.z)
}
}
data class LocationReplacements(
val lookup: Map<Block, List<BlockReplacement>>
)
data class LocationReplacements(
val lookup: Map<Block, List<BlockReplacement>>
)
data class BlockReplacement(
val checks: List<Area>?,
val replacement: Replacement,
) {
val roughCheck by lazy(LazyThreadSafetyMode.NONE) {
if (checks == null || checks.size < 3) return@lazy null
checks.reduce { acc, next -> acc.roughJoin(next) }
}
}
data class BlockReplacement(
val checks: List<Area>?,
val replacement: Replacement,
) {
val roughCheck by lazy(LazyThreadSafetyMode.NONE) {
if (checks == null || checks.size < 3) return@lazy null
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() {
val location = SBData.skyblockLocation
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()
}
}
}
var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf())
var currentIslandReplacements: LocationReplacements? = null
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 refreshReplacements() {
val location = SBData.skyblockLocation
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 {
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 {
if (blockPos == null) return true
val rc = replacement.roughCheck
if (rc != null && !rc.contains(blockPos)) return false
val areas = replacement.checks
if (areas != null && !areas.any { it.contains(blockPos) }) return false
return true
}
fun matchesPosition(replacement: BlockReplacement, blockPos: BlockPos?): Boolean {
if (blockPos == null) return true
val rc = replacement.roughCheck
if (rc != null && !rc.contains(blockPos)) return false
val areas = replacement.checks
if (areas != null && !areas.any { it.contains(blockPos) }) return false
return true
}
@JvmStatic
fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? {
return getReplacement(block, blockPos)?.bakedModel
}
@JvmStatic
fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BlockStateModel? {
return getReplacement(block, blockPos)?.blockModel
}
@JvmStatic
fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? {
if (isInFallback() && blockPos == null) {
return null
}
val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null
for (replacement in replacements) {
if (replacement.checks == null || matchesPosition(replacement, blockPos))
return replacement.replacement
}
return null
}
@JvmStatic
fun getReplacement(block: BlockState, blockPos: BlockPos?): Replacement? {
if (isInFallback() && blockPos == null) {
return null
}
val replacements = currentIslandReplacements?.lookup?.get(block.block) ?: return null
for (replacement in replacements) {
if (replacement.checks == null || matchesPosition(replacement, blockPos))
return replacement.replacement
}
return null
}
@Subscribe
fun onLocation(event: SkyblockServerUpdateEvent) {
refreshReplacements()
}
@Subscribe
fun onLocation(event: SkyblockServerUpdateEvent) {
refreshReplacements()
}
@Volatile
var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements(
mapOf()))
@Volatile
var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements(
mapOf()))
val insideFallbackCall = ThreadLocal.withInitial { 0 }
val insideFallbackCall = ThreadLocal.withInitial { 0 }
@JvmStatic
fun enterFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() + 1)
}
@JvmStatic
fun enterFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() + 1)
}
fun isInFallback() = insideFallbackCall.get() > 0
fun isInFallback() = insideFallbackCall.get() > 0
@JvmStatic
fun exitFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() - 1)
}
@JvmStatic
fun exitFallbackCall() {
insideFallbackCall.set(insideFallbackCall.get() - 1)
}
@Subscribe
fun onEarlyReload(event: EarlyResourceReloadEvent) {
preparationFuture = CompletableFuture
.supplyAsync(
{ prepare(event.resourceManager) }, event.preparationExecutor)
}
@Subscribe
fun onEarlyReload(event: EarlyResourceReloadEvent) {
preparationFuture = CompletableFuture
.supplyAsync(
{ prepare(event.resourceManager) }, event.preparationExecutor)
}
private fun prepare(manager: ResourceManager): BakedReplacements {
val resources = manager.findResources("overrides/blocks") {
it.namespace == "firmskyblock" && it.path.endsWith(".json")
}
val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>()
for ((file, resource) in resources) {
val json =
Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream)
.getOrElse { ex ->
logger.error("Failed to load block texture override at $file", ex)
continue
}
for (mode in json.modes) {
val island = SkyBlockIsland.forMode(mode)
val islandMpa = map.getOrPut(island, ::mutableMapOf)
for ((blockId, replacement) in json.replacements) {
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
.getOrNull()
if (block == null) {
logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'")
continue
}
val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf)
replacements.add(BlockReplacement(json.area, replacement))
}
}
}
private fun prepare(manager: ResourceManager): BakedReplacements {
val resources = manager.findResources("overrides/blocks") {
it.namespace == "firmskyblock" && it.path.endsWith(".json")
}
val map = mutableMapOf<SkyBlockIsland, MutableMap<Block, MutableList<BlockReplacement>>>()
for ((file, resource) in resources) {
val json =
Firmament.tryDecodeJsonFromStream<CustomBlockOverride>(resource.inputStream)
.getOrElse { ex ->
logger.error("Failed to load block texture override at $file", ex)
continue
}
for (mode in json.modes) {
val island = SkyBlockIsland.forMode(mode)
val islandMpa = map.getOrPut(island, ::mutableMapOf)
for ((blockId, replacement) in json.replacements) {
val block = MC.defaultRegistries.getOrThrow(RegistryKeys.BLOCK)
.getOptional(RegistryKey.of(RegistryKeys.BLOCK, blockId))
.getOrNull()
if (block == null) {
logger.error("Failed to load block texture override at ${file}: unknown block '$blockId'")
continue
}
val replacements = islandMpa.getOrPut(block.value(), ::mutableListOf)
replacements.add(BlockReplacement(json.area, replacement))
}
}
}
return BakedReplacements(map.mapValues { LocationReplacements(it.value) })
}
return BakedReplacements(map.mapValues { LocationReplacements(it.value) })
}
@Subscribe
fun onStart(event: FinalizeResourceManagerEvent) {
event.resourceManager.registerReloader(object :
SinglePreparationResourceReloader<BakedReplacements>() {
override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements {
return preparationFuture.join()
}
@Subscribe
fun onStart(event: FinalizeResourceManagerEvent) {
event.resourceManager.registerReloader(object :
SinglePreparationResourceReloader<BakedReplacements>() {
override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements {
return preparationFuture.join().also {
it.modelBakingFuture.join()
}
}
override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) {
allLocationReplacements = prepared
refreshReplacements()
}
})
}
override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) {
allLocationReplacements = prepared
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 moe.nea.firmament.features.texturepack.CustomBlockTextures;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.chunk.SectionBuilder;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BlockStateModel;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
@@ -16,24 +14,24 @@ import org.spongepowered.asm.mixin.injection.At;
@Mixin(SectionBuilder.class)
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;"))
private BlockStateModel replaceModelInRenderBlock(BlockRenderManager instance, BlockState state, Operation<BlockStateModel> original, @Local(argsOnly = true) BlockPos pos) {
var replacement = CustomBlockTextures.getReplacementModel(state, pos);
if (replacement != null) return replacement;
CustomBlockTextures.enterFallbackCall();
var fallback = original.call(instance, state);
CustomBlockTextures.exitFallbackCall();
return fallback;
}
@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(
BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) {
var replacement = CustomBlockTextures.getReplacementModel(state, pos);
if (replacement != null) return replacement;
CustomBlockTextures.enterFallbackCall();
var fallback = original.call(instance, state);
CustomBlockTextures.exitFallbackCall();
return fallback;
}
@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(ordinal = 2) BlockPos pos) {
var replacement = CustomBlockTextures.getReplacementModel(state, pos);
if (replacement != null) return replacement;
CustomBlockTextures.enterFallbackCall();
var fallback = original.call(instance, state);
CustomBlockTextures.exitFallbackCall();
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;"))
// private BakedModel replaceModelInRenderDamage(
// BlockModels instance, BlockState state, Operation<BakedModel> original, @Local(argsOnly = true) BlockPos pos) {
// var replacement = CustomBlockTextures.getReplacementModel(state, pos);
// if (replacement != null) return replacement;
// CustomBlockTextures.enterFallbackCall();
// var fallback = original.call(instance, state);
// CustomBlockTextures.exitFallbackCall();
// return fallback;
// }
}

View File

@@ -3,7 +3,7 @@ package moe.nea.firmament.mixins.custommodels;
import moe.nea.firmament.features.texturepack.CustomBlockTextures;
import net.minecraft.block.BlockState;
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.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class ReplaceFallbackBlockModel {
// TODO: add check to BlockDustParticle
@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);
if (replacement != null)
cir.setReturnValue(replacement);

View File

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