feat: Add generic component matcher
This commit is contained in:
@@ -17,6 +17,7 @@ import moe.nea.firmament.features.texturepack.predicates.AndPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.CastPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.DisplayNamePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ExtraAttributesPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.GenericComponentPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.ItemPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.LorePredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.NotPredicate
|
||||
@@ -63,6 +64,7 @@ object CustomModelOverrideParser {
|
||||
registerPredicateParser("item", ItemPredicate.Parser)
|
||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||
registerPredicateParser("pet", PetPredicate.Parser)
|
||||
registerPredicateParser("component", GenericComponentPredicate.Parser)
|
||||
}
|
||||
|
||||
private val neverPredicate = listOf(
|
||||
|
||||
@@ -32,7 +32,7 @@ fun interface NbtMatcher {
|
||||
return MatchStringExact(string)
|
||||
}
|
||||
if (jsonElement.isNumber) {
|
||||
return MatchNumberExact(jsonElement.asLong) //TODO: parse generic number
|
||||
return MatchNumberExact(jsonElement.asLong) // TODO: parse generic number
|
||||
}
|
||||
}
|
||||
if (jsonElement is JsonObject) {
|
||||
@@ -58,7 +58,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
INT("int") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asInt },
|
||||
{ (it as? NbtInt)?.intValue() },
|
||||
{ a, b ->
|
||||
@@ -70,7 +71,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
FLOAT("float") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asFloat },
|
||||
{ (it as? NbtFloat)?.floatValue() },
|
||||
{ a, b ->
|
||||
@@ -82,7 +84,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
DOUBLE("double") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asDouble },
|
||||
{ (it as? NbtDouble)?.doubleValue() },
|
||||
{ a, b ->
|
||||
@@ -94,7 +97,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
LONG("long") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asLong },
|
||||
{ (it as? NbtLong)?.longValue() },
|
||||
{ a, b ->
|
||||
@@ -106,7 +110,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
SHORT("short") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asShort },
|
||||
{ (it as? NbtShort)?.shortValue() },
|
||||
{ a, b ->
|
||||
@@ -118,7 +123,8 @@ fun interface NbtMatcher {
|
||||
},
|
||||
BYTE("byte") {
|
||||
override fun parse(element: JsonElement): NbtMatcher? {
|
||||
return parseGenericNumber(element,
|
||||
return parseGenericNumber(
|
||||
element,
|
||||
{ it.asByte },
|
||||
{ (it as? NbtByte)?.byteValue() },
|
||||
{ a, b ->
|
||||
@@ -193,7 +199,7 @@ fun interface NbtMatcher {
|
||||
|
||||
class MatchStringExact(val string: String) : NbtMatcher {
|
||||
override fun matches(nbt: NbtElement): Boolean {
|
||||
return nbt is NbtString && nbt.asString() == string
|
||||
return nbt.asString() == string
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -203,7 +209,7 @@ fun interface NbtMatcher {
|
||||
|
||||
class MatchString(val string: StringMatcher) : NbtMatcher {
|
||||
override fun matches(nbt: NbtElement): Boolean {
|
||||
return nbt is NbtString && string.matches(nbt.asString())
|
||||
return nbt.asString().let(string::matches)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -221,14 +227,10 @@ data class ExtraAttributesPredicate(
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? {
|
||||
if (jsonElement !is JsonObject) return null
|
||||
val path = jsonElement.get("path") ?: return null
|
||||
val pathSegments = if (path is JsonArray) {
|
||||
path.map { (it as JsonPrimitive).asString }
|
||||
} else if (path is JsonPrimitive && path.isString) {
|
||||
path.asString.split(".")
|
||||
} else return null
|
||||
val prism = NbtPrism.fromElement(path) ?: return null
|
||||
val matcher = NbtMatcher.Parser.parse(jsonElement.get("match") ?: jsonElement)
|
||||
?: return null
|
||||
return ExtraAttributesPredicate(NbtPrism(pathSegments), matcher)
|
||||
return ExtraAttributesPredicate(prism, matcher)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,9 +241,21 @@ data class ExtraAttributesPredicate(
|
||||
}
|
||||
|
||||
class NbtPrism(val path: List<String>) {
|
||||
companion object {
|
||||
fun fromElement(path: JsonElement): NbtPrism? {
|
||||
if (path is JsonArray) {
|
||||
return NbtPrism(path.map { (it as JsonPrimitive).asString })
|
||||
} else if (path is JsonPrimitive && path.isString) {
|
||||
return NbtPrism(path.asString.split("."))
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Prism($path)"
|
||||
}
|
||||
|
||||
fun access(root: NbtElement): Collection<NbtElement> {
|
||||
var rootSet = mutableListOf(root)
|
||||
var switch = mutableListOf<NbtElement>()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.serialization.Codec
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.component.ComponentType
|
||||
import net.minecraft.component.type.NbtComponent
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.util.Identifier
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
data class GenericComponentPredicate<T>(
|
||||
val componentType: ComponentType<T>,
|
||||
val codec: Codec<T>,
|
||||
val path: NbtPrism,
|
||||
val matcher: NbtMatcher,
|
||||
) : FirmamentModelPredicate {
|
||||
constructor(componentType: ComponentType<T>, path: NbtPrism, matcher: NbtMatcher)
|
||||
: this(componentType, componentType.codecOrThrow, path, matcher)
|
||||
|
||||
override fun test(stack: ItemStack, holder: LivingEntity?): Boolean {
|
||||
val component = stack.get(componentType) ?: return false
|
||||
// TODO: cache this
|
||||
val nbt =
|
||||
if (component is NbtComponent) component.nbt
|
||||
else codec.encodeStart(NbtOps.INSTANCE, component)
|
||||
.resultOrPartial().getOrNull() ?: return false
|
||||
return path.access(nbt).any { matcher.matches(it) }
|
||||
}
|
||||
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): GenericComponentPredicate<*>? {
|
||||
if (jsonElement !is JsonObject) return null
|
||||
val path = jsonElement.get("path") ?: return null
|
||||
val prism = NbtPrism.fromElement(path) ?: return null
|
||||
val matcher = NbtMatcher.Parser.parse(jsonElement.get("match") ?: jsonElement)
|
||||
?: return null
|
||||
val component = MC.currentOrDefaultRegistries
|
||||
.getOrThrow(RegistryKeys.DATA_COMPONENT_TYPE)
|
||||
.getOrThrow(
|
||||
RegistryKey.of(
|
||||
RegistryKeys.DATA_COMPONENT_TYPE,
|
||||
Identifier.of(jsonElement.get("component").asString)
|
||||
)
|
||||
).value()
|
||||
return GenericComponentPredicate(component, prism, matcher)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -167,6 +167,32 @@ Sub object match:
|
||||
}
|
||||
```
|
||||
|
||||
#### Components
|
||||
|
||||
You can match generic components similarly to [extra attributes](#extra-attributes). If you want to match an extra
|
||||
attribute match directly using that, for better performance.
|
||||
|
||||
You can specify a `path` and match similar to extra attributes, but in addition you can also specify a `component`. This
|
||||
variable is the identifier of a component type that will then be encoded to nbt and matched according to the `match`
|
||||
using a [nbt matcher](#nbt-matcher).
|
||||
|
||||
```json5
|
||||
"firmament:component": {
|
||||
"path": "rgb",
|
||||
"component": "minecraft:dyed_color",
|
||||
"int": 255
|
||||
}
|
||||
// Alternatively
|
||||
"firmament:component": {
|
||||
"path": "rgb",
|
||||
"component": "minecraft:dyed_color",
|
||||
"match": {
|
||||
"int": 255
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Pet Data
|
||||
|
||||
Filter by pet information. While you can already filter by the skyblock id for pet type and tier, this allows you to
|
||||
|
||||
Reference in New Issue
Block a user