Add custom model predicates
Add regex support Add and and or predicates
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.events
|
||||
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
|
||||
data class FeaturesInitializedEvent(val features: List<FirmamentFeature>) : FirmamentEvent() {
|
||||
companion object : FirmamentEventBus<FeaturesInitializedEvent>()
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
@@ -9,6 +10,7 @@ package moe.nea.firmament.features
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.serializer
|
||||
import moe.nea.firmament.Firmament
|
||||
import moe.nea.firmament.events.FeaturesInitializedEvent
|
||||
import moe.nea.firmament.features.chat.AutoCompletions
|
||||
import moe.nea.firmament.features.chat.ChatLinks
|
||||
import moe.nea.firmament.features.chat.QuickCommands
|
||||
@@ -76,6 +78,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
||||
loadFeature(DebugView)
|
||||
}
|
||||
allFeatures.forEach { it.config }
|
||||
FeaturesInitializedEvent.publish(FeaturesInitializedEvent(allFeatures.toList()))
|
||||
hasAutoloaded = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.notifications
|
||||
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
|
||||
object Notifications {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class AndPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return children.all { it.test(stack) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
val children =
|
||||
(jsonElement as JsonArray)
|
||||
.flatMap {
|
||||
CustomModelOverrideParser.parsePredicates(it as JsonObject)
|
||||
}
|
||||
.toTypedArray()
|
||||
return AndPredicate(children)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
interface BakedOverrideData {
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object CustomModelOverrideParser {
|
||||
|
||||
val predicateParsers = mutableMapOf<Identifier, FirmamentModelPredicateParser>()
|
||||
|
||||
|
||||
fun registerPredicateParser(name: String, parser: FirmamentModelPredicateParser) {
|
||||
predicateParsers[Identifier("firmament", name)] = parser
|
||||
}
|
||||
|
||||
init {
|
||||
registerPredicateParser("display_name", DisplayNamePredicate.Parser)
|
||||
registerPredicateParser("lore", LorePredicate.Parser)
|
||||
registerPredicateParser("all", AndPredicate.Parser)
|
||||
registerPredicateParser("any", OrPredicate.Parser)
|
||||
}
|
||||
|
||||
fun parsePredicates(predicates: JsonObject): List<FirmamentModelPredicate> {
|
||||
val parsedPredicates = mutableListOf<FirmamentModelPredicate>()
|
||||
for (predicateName in predicates.keySet()) {
|
||||
if (!predicateName.startsWith("firmament:")) continue
|
||||
val identifier = Identifier(predicateName)
|
||||
val parser = predicateParsers[identifier] ?: continue
|
||||
val parsedPredicate = parser.parse(predicates[predicateName])
|
||||
parsedPredicates.add(parsedPredicate)
|
||||
}
|
||||
return parsedPredicates
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseCustomModelOverrides(jsonObject: JsonObject): Array<FirmamentModelPredicate>? {
|
||||
val predicates = (jsonObject["predicate"] as? JsonObject) ?: return null
|
||||
val parsedPredicates = parsePredicates(predicates)
|
||||
if (parsedPredicates.isEmpty())
|
||||
return null
|
||||
return parsedPredicates.toTypedArray()
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import net.minecraft.block.SkullBlock
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.texture.PlayerSkinProvider
|
||||
import net.minecraft.client.util.ModelIdentifier
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.events.CustomItemModelEvent
|
||||
@@ -33,6 +32,7 @@ object CustomSkyBlockTextures : FirmamentFeature {
|
||||
val enabled by toggle("enabled") { true }
|
||||
val skullsEnabled by toggle("skulls-enabled") { true }
|
||||
val cacheDuration by integer("cache-duration", 0, 20) { 1 }
|
||||
val enableModelOverrides by toggle("model-overrides") { true }
|
||||
}
|
||||
|
||||
override val config: ManagedConfig
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtString
|
||||
|
||||
data class DisplayNamePredicate(val stringMatcher: StringMatcher) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
val display = stack.getOrCreateSubNbt(ItemStack.DISPLAY_KEY)
|
||||
return if (display.contains(ItemStack.NAME_KEY, NbtElement.STRING_TYPE.toInt()))
|
||||
stringMatcher.matches(display.get(ItemStack.NAME_KEY) as NbtString)
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return DisplayNamePredicate(StringMatcher.parse(jsonElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
interface FirmamentModelPredicate {
|
||||
fun test(stack: ItemStack): Boolean
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
|
||||
interface FirmamentModelPredicateParser {
|
||||
fun parse(jsonElement: JsonElement): FirmamentModelPredicate
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtString
|
||||
|
||||
class LorePredicate(val matcher: StringMatcher) : FirmamentModelPredicate {
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
return LorePredicate(StringMatcher.parse(jsonElement))
|
||||
}
|
||||
}
|
||||
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
val display = stack.getOrCreateSubNbt(ItemStack.DISPLAY_KEY)
|
||||
if (!display.contains(ItemStack.LORE_KEY, NbtElement.LIST_TYPE.toInt()))
|
||||
return false
|
||||
val lore = display.getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE.toInt())
|
||||
return lore.any { matcher.matches(it as NbtString)}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
interface ModelOverrideData {
|
||||
fun getFirmamentOverrides(): Array<FirmamentModelPredicate>?
|
||||
fun setFirmamentOverrides(overrides: Array<FirmamentModelPredicate>?)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import moe.nea.firmament.util.filter.IteratorFilterSet
|
||||
|
||||
class ModelOverrideFilterSet(original: java.util.Set<Map.Entry<String, JsonElement>>) :
|
||||
IteratorFilterSet<Map.Entry<String, JsonElement>>(original) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun createFilterSet(set: java.util.Set<*>): java.util.Set<*> {
|
||||
return ModelOverrideFilterSet(set as java.util.Set<Map.Entry<String, JsonElement>>) as java.util.Set<*>
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldKeepElement(element: Map.Entry<String, JsonElement>): Boolean {
|
||||
return !element.key.startsWith("firmament:")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class OrPredicate(val children: Array<FirmamentModelPredicate>) : FirmamentModelPredicate {
|
||||
override fun test(stack: ItemStack): Boolean {
|
||||
return children.any { it.test(stack) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate {
|
||||
val children =
|
||||
(jsonElement as JsonArray)
|
||||
.flatMap {
|
||||
CustomModelOverrideParser.parsePredicates(it as JsonObject)
|
||||
}
|
||||
.toTypedArray()
|
||||
return AndPredicate(children)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.features.texturepack
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import java.util.function.Predicate
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.text.Text
|
||||
import moe.nea.firmament.util.removeColorCodes
|
||||
|
||||
interface StringMatcher {
|
||||
fun matches(string: String): Boolean
|
||||
fun matches(text: Text): Boolean {
|
||||
return matches(text.string)
|
||||
}
|
||||
|
||||
fun matches(nbt: NbtString): Boolean {
|
||||
val string = nbt.asString()
|
||||
val jsonStart = string.indexOf('{')
|
||||
val stringStart = string.indexOf('"')
|
||||
val isString = stringStart >= 0 && string.subSequence(0, stringStart).isBlank()
|
||||
val isJson = jsonStart >= 0 && string.subSequence(0, jsonStart).isBlank()
|
||||
if (isString || isJson)
|
||||
return matches(Text.Serialization.fromJson(string) ?: return false)
|
||||
return matches(string)
|
||||
}
|
||||
|
||||
class Equals(input: String, val stripColorCodes: Boolean) : StringMatcher {
|
||||
private val expected = if (stripColorCodes) input.removeColorCodes() else input
|
||||
override fun matches(string: String): Boolean {
|
||||
return expected == (if (stripColorCodes) string.removeColorCodes() else string)
|
||||
}
|
||||
}
|
||||
|
||||
class Pattern(patternWithColorCodes: String, val stripColorCodes: Boolean) : StringMatcher {
|
||||
private val regex: Predicate<String> = patternWithColorCodes.toPattern().asMatchPredicate()
|
||||
override fun matches(string: String): Boolean {
|
||||
return regex.test(if (stripColorCodes) string.removeColorCodes() else string)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(jsonElement: JsonElement): StringMatcher {
|
||||
if (jsonElement is JsonPrimitive) {
|
||||
return Equals(jsonElement.asString, true)
|
||||
}
|
||||
if (jsonElement is JsonObject) {
|
||||
val regex = jsonElement["regex"] as JsonPrimitive?
|
||||
val text = jsonElement["text"] as JsonPrimitive?
|
||||
val shouldStripColor = when (val color = (jsonElement["color"] as JsonPrimitive?)?.asString) {
|
||||
"preserve" -> false
|
||||
"strip", null -> true
|
||||
else -> error("Unknown color preservation mode: $color")
|
||||
}
|
||||
if ((regex == null) == (text == null)) error("Could not parse $jsonElement as string matcher")
|
||||
if (regex != null)
|
||||
return Pattern(regex.asString, shouldStripColor)
|
||||
if (text != null)
|
||||
return Equals(text.asString, shouldStripColor)
|
||||
}
|
||||
error("Could not parse $jsonElement as a string matcher")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package moe.nea.firmament.util.filter
|
||||
|
||||
abstract class IteratorFilterSet<K>(val original: java.util.Set<K>) : java.util.Set<K> by original {
|
||||
abstract fun shouldKeepElement(element: K): Boolean
|
||||
|
||||
override fun iterator(): MutableIterator<K> {
|
||||
val parentIterator = original.iterator()
|
||||
return object : MutableIterator<K> {
|
||||
var lastEntry: K? = null
|
||||
override fun hasNext(): Boolean {
|
||||
while (lastEntry == null) {
|
||||
if (!parentIterator.hasNext())
|
||||
break
|
||||
val element = parentIterator.next()
|
||||
if (!shouldKeepElement(element)) continue
|
||||
lastEntry = element
|
||||
}
|
||||
return lastEntry != null
|
||||
}
|
||||
|
||||
override fun next(): K {
|
||||
if (!hasNext()) throw NoSuchElementException()
|
||||
return lastEntry ?: throw NoSuchElementException()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
@@ -69,9 +70,30 @@ class TextMatcher(text: Text) {
|
||||
}
|
||||
}
|
||||
|
||||
val formattingChars = "kmolnrKMOLNR".toSet()
|
||||
fun CharSequence.removeColorCodes(keepNonColorCodes: Boolean = false): String {
|
||||
var nextParagraph = indexOf('§')
|
||||
if (nextParagraph < 0) return this.toString()
|
||||
val stringBuffer = StringBuilder(this.length)
|
||||
var readIndex = 0
|
||||
while (nextParagraph >= 0) {
|
||||
stringBuffer.append(this, readIndex, nextParagraph)
|
||||
if (keepNonColorCodes && nextParagraph + 1 < length && this[nextParagraph + 1] in formattingChars) {
|
||||
readIndex = nextParagraph
|
||||
nextParagraph = indexOf('§', startIndex = readIndex + 1)
|
||||
} else {
|
||||
readIndex = nextParagraph + 2
|
||||
nextParagraph = indexOf('§', startIndex = readIndex)
|
||||
}
|
||||
if (readIndex > this.length)
|
||||
readIndex = this.length
|
||||
}
|
||||
stringBuffer.append(this, readIndex, this.length)
|
||||
return stringBuffer.toString()
|
||||
}
|
||||
|
||||
val Text.unformattedString
|
||||
get() = string.replace("§.".toRegex(), "")
|
||||
val Text.unformattedString: String
|
||||
get() = string.removeColorCodes().toString()
|
||||
|
||||
|
||||
fun Text.transformEachRecursively(function: (Text) -> Text): Text {
|
||||
|
||||
Reference in New Issue
Block a user