aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/features')
-rw-r--r--src/main/kotlin/features/FeatureManager.kt2
-rw-r--r--src/main/kotlin/features/chat/ChatLinks.kt239
-rw-r--r--src/main/kotlin/features/debug/PowerUserTools.kt11
-rw-r--r--src/main/kotlin/features/texturepack/BakedModelExtra.kt30
-rw-r--r--src/main/kotlin/features/texturepack/BakedOverrideData.kt14
-rw-r--r--src/main/kotlin/features/texturepack/CustomBlockTextures.kt301
-rw-r--r--src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt155
-rw-r--r--src/main/kotlin/features/texturepack/CustomGlobalTextures.kt164
-rw-r--r--src/main/kotlin/features/texturepack/CustomModelOverrideParser.kt82
-rw-r--r--src/main/kotlin/features/texturepack/CustomSkyBlockTextures.kt135
-rw-r--r--src/main/kotlin/features/texturepack/CustomTextColors.kt66
-rw-r--r--src/main/kotlin/features/texturepack/FirmamentModelPredicate.kt8
-rw-r--r--src/main/kotlin/features/texturepack/FirmamentModelPredicateParser.kt8
-rw-r--r--src/main/kotlin/features/texturepack/JsonUnbakedModelFirmExtra.kt16
-rw-r--r--src/main/kotlin/features/texturepack/ModelOverrideData.kt15
-rw-r--r--src/main/kotlin/features/texturepack/RarityMatcher.kt69
-rw-r--r--src/main/kotlin/features/texturepack/StringMatcher.kt159
-rw-r--r--src/main/kotlin/features/texturepack/TintOverrides.kt75
-rw-r--r--src/main/kotlin/features/texturepack/predicates/AlwaysPredicate.kt19
-rw-r--r--src/main/kotlin/features/texturepack/predicates/AndPredicate.kt28
-rw-r--r--src/main/kotlin/features/texturepack/predicates/DisplayNamePredicate.kt22
-rw-r--r--src/main/kotlin/features/texturepack/predicates/ExtraAttributesPredicate.kt271
-rw-r--r--src/main/kotlin/features/texturepack/predicates/ItemPredicate.kt34
-rw-r--r--src/main/kotlin/features/texturepack/predicates/LorePredicate.kt22
-rw-r--r--src/main/kotlin/features/texturepack/predicates/NotPredicate.kt21
-rw-r--r--src/main/kotlin/features/texturepack/predicates/NumberMatcher.kt124
-rw-r--r--src/main/kotlin/features/texturepack/predicates/OrPredicate.kt29
-rw-r--r--src/main/kotlin/features/texturepack/predicates/PetPredicate.kt66
28 files changed, 126 insertions, 2059 deletions
diff --git a/src/main/kotlin/features/FeatureManager.kt b/src/main/kotlin/features/FeatureManager.kt
index 2110d09..0f5ebf8 100644
--- a/src/main/kotlin/features/FeatureManager.kt
+++ b/src/main/kotlin/features/FeatureManager.kt
@@ -29,7 +29,6 @@ import moe.nea.firmament.features.inventory.buttons.InventoryButtons
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
import moe.nea.firmament.features.mining.PickaxeAbility
import moe.nea.firmament.features.mining.PristineProfitTracker
-import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.features.world.Waypoints
import moe.nea.firmament.util.data.DataHolder
@@ -70,7 +69,6 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
loadFeature(QuickCommands)
loadFeature(PetFeatures)
loadFeature(SaveCursorPosition)
- loadFeature(CustomSkyBlockTextures)
loadFeature(PriceData)
loadFeature(Fixes)
loadFeature(DianaWaypoints)
diff --git a/src/main/kotlin/features/chat/ChatLinks.kt b/src/main/kotlin/features/chat/ChatLinks.kt
index 5bce3f4..f85825b 100644
--- a/src/main/kotlin/features/chat/ChatLinks.kt
+++ b/src/main/kotlin/features/chat/ChatLinks.kt
@@ -1,5 +1,3 @@
-
-
package moe.nea.firmament.features.chat
import io.ktor.client.request.get
@@ -7,16 +5,15 @@ import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.javaio.toInputStream
import java.net.URL
import java.util.Collections
+import java.util.concurrent.atomic.AtomicInteger
import moe.nea.jarvis.api.Point
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlin.math.min
import net.minecraft.client.gui.screen.ChatScreen
-import net.minecraft.client.render.RenderLayer
import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture
-import net.minecraft.scoreboard.ScoreboardCriterion.RenderType
import net.minecraft.text.ClickEvent
import net.minecraft.text.HoverEvent
import net.minecraft.text.Style
@@ -35,130 +32,132 @@ import moe.nea.firmament.util.transformEachRecursively
import moe.nea.firmament.util.unformattedString
object ChatLinks : FirmamentFeature {
- override val identifier: String
- get() = "chat-links"
+ override val identifier: String
+ get() = "chat-links"
- object TConfig : ManagedConfig(identifier, Category.CHAT) {
- val enableLinks by toggle("links-enabled") { true }
- val imageEnabled by toggle("image-enabled") { true }
- val allowAllHosts by toggle("allow-all-hosts") { false }
- val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
- val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
- val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
- }
+ object TConfig : ManagedConfig(identifier, Category.CHAT) {
+ val enableLinks by toggle("links-enabled") { true }
+ val imageEnabled by toggle("image-enabled") { true }
+ val allowAllHosts by toggle("allow-all-hosts") { false }
+ val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" }
+ val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() }
+ val position by position("position", 16 * 20, 9 * 20) { Point(0.0, 0.0) }
+ }
- private fun isHostAllowed(host: String) =
- TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
+ private fun isHostAllowed(host: String) =
+ TConfig.allowAllHosts || TConfig.actualAllowedHosts.any { it.equals(host, ignoreCase = true) }
- private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
+ private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/"))
- override val config get() = TConfig
- val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
+ override val config get() = TConfig
+ val urlRegex = "https://[^. ]+\\.[^ ]+(\\.?( |$))".toRegex()
+ val nextTexId = AtomicInteger(0)
- data class Image(
- val texture: Identifier,
- val width: Int,
- val height: Int,
- )
+ data class Image(
+ val texture: Identifier,
+ val width: Int,
+ val height: Int,
+ )
- val imageCache: MutableMap<String, Deferred<Image?>> =
- Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
+ val imageCache: MutableMap<String, Deferred<Image?>> =
+ Collections.synchronizedMap(mutableMapOf<String, Deferred<Image?>>())
- private fun tryCacheUrl(url: String) {
- if (!isUrlAllowed(url)) {
- return
- }
- if (url in imageCache) {
- return
- }
- imageCache[url] = Firmament.coroutineScope.async {
- try {
- val response = Firmament.httpClient.get(URL(url))
- if (response.status.value == 200) {
- val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
- val image = NativeImage.read(inputStream)
- val texture = MC.textureManager.registerDynamicTexture(
- "dynamic_image_preview",
- NativeImageBackedTexture(image)
- )
- Image(texture, image.width, image.height)
- } else
- null
- } catch (exc: Exception) {
- exc.printStackTrace()
- null
- }
- }
- }
+ private fun tryCacheUrl(url: String) {
+ if (!isUrlAllowed(url)) {
+ return
+ }
+ if (url in imageCache) {
+ return
+ }
+ imageCache[url] = Firmament.coroutineScope.async {
+ try {
+ val response = Firmament.httpClient.get(URL(url))
+ if (response.status.value == 200) {
+ val inputStream = response.bodyAsChannel().toInputStream(Firmament.globalJob)
+ val image = NativeImage.read(inputStream)
+ val texId = Firmament.identifier("dynamic_image_preview${nextTexId.getAndIncrement()}")
+ MC.textureManager.registerTexture(
+ texId,
+ NativeImageBackedTexture(image)
+ )
+ Image(texId, image.width, image.height)
+ } else
+ null
+ } catch (exc: Exception) {
+ exc.printStackTrace()
+ null
+ }
+ }
+ }
- val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
- fun isImageUrl(url: String): Boolean {
- return (url.substringAfterLast('.').lowercase() in imageExtensions)
- }
+ val imageExtensions = listOf("jpg", "png", "gif", "jpeg")
+ fun isImageUrl(url: String): Boolean {
+ return (url.substringAfterLast('.').lowercase() in imageExtensions)
+ }
- @Subscribe
- @OptIn(ExperimentalCoroutinesApi::class)
- fun onRender(it: ScreenRenderPostEvent) {
- if (!TConfig.imageEnabled) return
- if (it.screen !is ChatScreen) return
- val hoveredComponent =
- MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
- val hoverEvent = hoveredComponent.hoverEvent ?: return
- val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
- val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
- if (!isImageUrl(url)) return
- val imageFuture = imageCache[url] ?: return
- if (!imageFuture.isCompleted) return
- val image = imageFuture.getCompleted() ?: return
- it.drawContext.matrices.push()
- val pos = TConfig.position
- pos.applyTransformations(it.drawContext.matrices)
- val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
- it.drawContext.matrices.scale(scale, scale, 1F)
- it.drawContext.drawTexture(
- image.texture,
- 0,
- 0,
- 1F,
- 1F,
- image.width,
- image.height,
- image.width,
- image.height,
- )
- it.drawContext.matrices.pop()
- }
+ @Subscribe
+ @OptIn(ExperimentalCoroutinesApi::class)
+ fun onRender(it: ScreenRenderPostEvent) {
+ if (!TConfig.imageEnabled) return
+ if (it.screen !is ChatScreen) return
+ val hoveredComponent =
+ MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return
+ val hoverEvent = hoveredComponent.hoverEvent ?: return
+ val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
+ val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return
+ if (!isImageUrl(url)) return
+ val imageFuture = imageCache[url] ?: return
+ if (!imageFuture.isCompleted) return
+ val image = imageFuture.getCompleted() ?: return
+ it.drawContext.matrices.push()
+ val pos = TConfig.position
+ pos.applyTransformations(it.drawContext.matrices)
+ val scale = min(1F, min((9 * 20F) / image.height, (16 * 20F) / image.width))
+ it.drawContext.matrices.scale(scale, scale, 1F)
+ it.drawContext.drawTexture(
+ image.texture,
+ 0,
+ 0,
+ 1F,
+ 1F,
+ image.width,
+ image.height,
+ image.width,
+ image.height,
+ )
+ it.drawContext.matrices.pop()
+ }
- @Subscribe
- fun onModifyChat(it: ModifyChatEvent) {
- if (!TConfig.enableLinks) return
- it.replaceWith = it.replaceWith.transformEachRecursively { child ->
- val text = child.string
- if ("://" !in text) return@transformEachRecursively child
- val s = Text.empty().setStyle(child.style)
- var index = 0
- while (index < text.length) {
- val nextMatch = urlRegex.find(text, index)
- if (nextMatch == null) {
- s.append(Text.literal(text.substring(index, text.length)))
- break
- }
- val range = nextMatch.groups[0]!!.range
- val url = nextMatch.groupValues[0]
- s.append(Text.literal(text.substring(index, range.first)))
- s.append(
- Text.literal(url).setStyle(
- Style.EMPTY.withUnderline(true).withColor(
- Formatting.AQUA
- ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
- .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
- )
- )
- if (isImageUrl(url))
- tryCacheUrl(url)
- index = range.last + 1
- }
- s
- }
- }
+ @Subscribe
+ fun onModifyChat(it: ModifyChatEvent) {
+ if (!TConfig.enableLinks) return
+ it.replaceWith = it.replaceWith.transformEachRecursively { child ->
+ val text = child.string
+ if ("://" !in text) return@transformEachRecursively child
+ val s = Text.empty().setStyle(child.style)
+ var index = 0
+ while (index < text.length) {
+ val nextMatch = urlRegex.find(text, index)
+ if (nextMatch == null) {
+ s.append(Text.literal(text.substring(index, text.length)))
+ break
+ }
+ val range = nextMatch.groups[0]!!.range
+ val url = nextMatch.groupValues[0]
+ s.append(Text.literal(text.substring(index, range.first)))
+ s.append(
+ Text.literal(url).setStyle(
+ Style.EMPTY.withUnderline(true).withColor(
+ Formatting.AQUA
+ ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url)))
+ .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url))
+ )
+ )
+ if (isImageUrl(url))
+ tryCacheUrl(url)
+ index = range.last + 1
+ }
+ s
+ }
+ }
}
diff --git a/src/main/kotlin/features/debug/PowerUserTools.kt b/src/main/kotlin/features/debug/PowerUserTools.kt
index 13320dc..225bc13 100644
--- a/src/main/kotlin/features/debug/PowerUserTools.kt
+++ b/src/main/kotlin/features/debug/PowerUserTools.kt
@@ -5,6 +5,7 @@ import kotlin.jvm.optionals.getOrNull
import net.minecraft.block.SkullBlock
import net.minecraft.block.entity.SkullBlockEntity
import net.minecraft.component.DataComponentTypes
+import net.minecraft.component.type.ProfileComponent
import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
@@ -12,6 +13,7 @@ import net.minecraft.item.Items
import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text
import net.minecraft.text.TextCodecs
+import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult
@@ -23,7 +25,6 @@ import moe.nea.firmament.events.ScreenChangeEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldKeyboardEvent
import moe.nea.firmament.features.FirmamentFeature
-import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils
@@ -101,6 +102,8 @@ object PowerUserTools : FirmamentFeature {
}
}
+ // TODO: leak this through some other way, maybe.
+ lateinit var getSkullId: (profile: ProfileComponent) -> Identifier?
@Subscribe
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
@@ -116,7 +119,7 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack =
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.skyblockid", sbId.neuItem))
} else if (it.matches(TConfig.copyTexturePackId)) {
- val model = CustomItemModelEvent.getModelIdentifier(item)
+ val model = CustomItemModelEvent.getModelIdentifier(item) // TODO: remove global texture overrides, maybe
if (model == null) {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.modelid.fail"))
return
@@ -146,7 +149,7 @@ object PowerUserTools : FirmamentFeature {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-profile"))
return
}
- val skullTexture = CustomSkyBlockTextures.getSkullTexture(profile)
+ val skullTexture = getSkullId(profile)
if (skullTexture == null) {
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.skull-id.fail.no-texture"))
return
@@ -179,7 +182,7 @@ object PowerUserTools : FirmamentFeature {
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
return
}
- val id = CustomSkyBlockTextures.getSkullTexture(entity.owner!!)
+ val id = getSkullId(entity.owner!!)
if (id == null) {
MC.sendChat(Text.translatable("firmament.tooltip.copied.skull.fail"))
} else {
diff --git a/src/main/kotlin/features/texturepack/BakedModelExtra.kt b/src/main/kotlin/features/texturepack/BakedModelExtra.kt
deleted file mode 100644
index 6305748..0000000
--- a/src/main/kotlin/features/texturepack/BakedModelExtra.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package moe.nea.firmament.features.texturepack
-
-import net.fabricmc.fabric.api.renderer.v1.model.WrapperBakedModel as WrapperBakedModelFabric
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.render.model.WrapperBakedModel
-import moe.nea.firmament.util.ErrorUtil
-
-interface BakedModelExtra {
- companion object {
- @JvmStatic
- fun cast(originalModel: BakedModel): BakedModelExtra? {
- var p = originalModel
- for (i in 0..256) {
- p = when (p) {
- is BakedModelExtra -> return p
- is WrapperBakedModel -> p.wrapped
- is WrapperBakedModelFabric -> WrapperBakedModelFabric.unwrap(p)
- else -> break
- }
- }
- ErrorUtil.softError("Could not find a baked model for $originalModel")
- return null
- }
- }
-
- var tintOverrides_firmament: TintOverrides?
-
- fun getHeadModel_firmament(): BakedModel?
- fun setHeadModel_firmament(headModel: BakedModel?)
-}
diff --git a/src/main/kotlin/features/texturepack/BakedOverrideData.kt b/src/main/kotlin/features/texturepack/BakedOverrideData.kt
deleted file mode 100644
index e9391f1..0000000
--- a/src/main/kotlin/features/texturepack/BakedOverrideData.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-
-package moe.nea.firmament.features.texturepack
-
-import net.minecraft.client.render.model.json.ModelOverrideList
-
-interface BakedOverrideData {
- fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
- fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
- companion object{
- @Suppress("CAST_NEVER_SUCCEEDS")
- @JvmStatic
- fun cast(bakedOverride: ModelOverrideList.BakedOverride): BakedOverrideData = bakedOverride as BakedOverrideData
- }
-}
diff --git a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/features/texturepack/CustomBlockTextures.kt
deleted file mode 100644
index 2f7f084..0000000
--- a/src/main/kotlin/features/texturepack/CustomBlockTextures.kt
+++ /dev/null
@@ -1,301 +0,0 @@
-@file:UseSerializers(BlockPosSerializer::class, IdentifierSerializer::class)
-
-package moe.nea.firmament.features.texturepack
-
-import java.util.concurrent.CompletableFuture
-import net.fabricmc.loader.api.FabricLoader
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.Transient
-import kotlinx.serialization.UseSerializers
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.JsonDecoder
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonPrimitive
-import kotlinx.serialization.serializer
-import kotlin.jvm.optionals.getOrNull
-import net.minecraft.block.Block
-import net.minecraft.block.BlockState
-import net.minecraft.client.render.model.BakedModel
-import net.minecraft.client.util.ModelIdentifier
-import net.minecraft.registry.RegistryKey
-import net.minecraft.registry.RegistryKeys
-import net.minecraft.resource.ResourceManager
-import net.minecraft.resource.SinglePreparationResourceReloader
-import net.minecraft.util.Identifier
-import net.minecraft.util.math.BlockPos
-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.SkyblockServerUpdateEvent
-import moe.nea.firmament.features.texturepack.CustomGlobalTextures.logger
-import moe.nea.firmament.util.IdentifierSerializer
-import moe.nea.firmament.util.MC
-import moe.nea.firmament.util.SBData
-import moe.nea.firmament.util.SkyBlockIsland
-import moe.nea.firmament.util.json.BlockPosSerializer
-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(with = Replacement.Serializer::class)
- data class Replacement(
- val block: Identifier,
- val sound: Identifier?,
- ) {
-
- @Transient
- val blockModelIdentifier get() = ModelIdentifier(block.withPrefixedPath("block/"), "firmament")
-
- @Transient
- val bakedModel: BakedModel by lazy(LazyThreadSafetyMode.NONE) {
- MC.instance.bakedModelManager.getModel(blockModelIdentifier)
- }
-
- @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
-
- 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)
- }
- }
- }
-
- @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),
- )
-
- 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)
- }
- }
-
- 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 BakedReplacements(val data: Map<SkyBlockIsland, LocationReplacements>)
-
- var allLocationReplacements: BakedReplacements = BakedReplacements(mapOf())
- var currentIslandReplacements: LocationReplacements? = 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
- }
-
- @JvmStatic
- fun getReplacementModel(block: BlockState, blockPos: BlockPos?): BakedModel? {
- return getReplacement(block, blockPos)?.bakedModel
- }
-
- @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()
- }
-
- @Volatile
- var preparationFuture: CompletableFuture<BakedReplacements> = CompletableFuture.completedFuture(BakedReplacements(
- mapOf()))
-
- val insideFallbackCall = ThreadLocal.withInitial { 0 }
-
- @JvmStatic
- fun enterFallbackCall() {
- insideFallbackCall.set(insideFallbackCall.get() + 1)
- }
-
- fun isInFallback() = insideFallbackCall.get() > 0
-
- @JvmStatic
- fun exitFallbackCall() {
- insideFallbackCall.set(insideFallbackCall.get() - 1)
- }
-
- @Subscribe
- fun onEarlyReload(event: EarlyResourceReloadEvent) {
- preparationFuture = CompletableFuture
- .supplyAsync(
- { prepare(event.resourceManager) }, event.preparationExecutor)
- }
-
- @Subscribe
- fun bakeExtraModels(event: BakeExtraModelsEvent) {
- preparationFuture.join().data.values
- .flatMap { it.lookup.values }
- .flatten()
- .mapTo(mutableSetOf()) { it.replacement.blockModelIdentifier }
- .forEach { event.addNonItemModel(it, it.id) }
- }
-
- 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) })
- }
-
- @JvmStatic
- fun patchIndigo(orig: BakedModel, pos: BlockPos, state: BlockState): BakedModel {
- return getReplacementModel(state, pos) ?: orig
- }
-
- @Subscribe
- fun onStart(event: FinalizeResourceManagerEvent) {
- event.resourceManager.registerReloader(object :
- SinglePreparationResourceReloader<BakedReplacements>() {
- override fun prepare(manager: ResourceManager, profiler: Profiler): BakedReplacements {
- return preparationFuture.join()
- }
-
- override fun apply(prepared: BakedReplacements, manager: ResourceManager, profiler: Profiler?) {
- allLocationReplacements = prepared
- refreshReplacements()
- }
- })
- }
-}
diff --git a/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt b/src/main/kotlin/features/texturepack/CustomGlobalArmorOverrides.kt
deleted file mode 100644
index 54e9e11..0000000
--- a/