feat: Add skull parser
This commit is contained in:
@@ -10,6 +10,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import moe.nea.firmament.util.parseDashlessUUID
|
||||
import moe.nea.firmament.util.parsePotentiallyDashlessUUID
|
||||
|
||||
object DashlessUUIDSerializer : KSerializer<UUID> {
|
||||
override val descriptor: SerialDescriptor =
|
||||
@@ -17,10 +18,7 @@ object DashlessUUIDSerializer : KSerializer<UUID> {
|
||||
|
||||
override fun deserialize(decoder: Decoder): UUID {
|
||||
val str = decoder.decodeString()
|
||||
if ("-" in str) {
|
||||
return UUID.fromString(str)
|
||||
}
|
||||
return parseDashlessUUID(str)
|
||||
return parsePotentiallyDashlessUUID(str)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: UUID) {
|
||||
|
||||
@@ -3,6 +3,12 @@ package moe.nea.firmament.util
|
||||
import java.math.BigInteger
|
||||
import java.util.UUID
|
||||
|
||||
fun parsePotentiallyDashlessUUID(unknownFormattedUUID: String): UUID {
|
||||
if ("-" in unknownFormattedUUID)
|
||||
return UUID.fromString(unknownFormattedUUID)
|
||||
return parseDashlessUUID(unknownFormattedUUID)
|
||||
}
|
||||
|
||||
fun parseDashlessUUID(dashlessUuid: String): UUID {
|
||||
val most = BigInteger(dashlessUuid.substring(0, 16), 16)
|
||||
val least = BigInteger(dashlessUuid.substring(16, 32), 16)
|
||||
|
||||
@@ -24,6 +24,7 @@ import moe.nea.firmament.features.texturepack.predicates.NotPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.OrPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.PullingPredicate
|
||||
import moe.nea.firmament.features.texturepack.predicates.SkullPredicate
|
||||
import moe.nea.firmament.util.json.KJsonOps
|
||||
|
||||
object CustomModelOverrideParser {
|
||||
@@ -65,6 +66,7 @@ object CustomModelOverrideParser {
|
||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||
registerPredicateParser("pet", PetPredicate.Parser)
|
||||
registerPredicateParser("component", GenericComponentPredicate.Parser)
|
||||
registerPredicateParser("skull", SkullPredicate.Parser)
|
||||
}
|
||||
|
||||
private val neverPredicate = listOf(
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package moe.nea.firmament.features.texturepack.predicates
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture
|
||||
import java.util.UUID
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import net.minecraft.component.DataComponentTypes
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicate
|
||||
import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser
|
||||
import moe.nea.firmament.features.texturepack.StringMatcher
|
||||
import moe.nea.firmament.util.mc.decodeProfileTextureProperty
|
||||
import moe.nea.firmament.util.parsePotentiallyDashlessUUID
|
||||
|
||||
class SkullPredicate(
|
||||
val profileId: UUID?,
|
||||
val textureProfileId: UUID?,
|
||||
val skinUrl: StringMatcher?,
|
||||
val textureValue: StringMatcher?,
|
||||
) : FirmamentModelPredicate {
|
||||
object Parser : FirmamentModelPredicateParser {
|
||||
override fun parse(jsonElement: JsonElement): FirmamentModelPredicate? {
|
||||
val obj = jsonElement.asJsonObject
|
||||
val profileId = obj.getAsJsonPrimitive("profileId")
|
||||
?.asString?.let(::parsePotentiallyDashlessUUID)
|
||||
val textureProfileId = obj.getAsJsonPrimitive("textureProfileId")
|
||||
?.asString?.let(::parsePotentiallyDashlessUUID)
|
||||
val textureValue = obj.get("textureValue")?.let(StringMatcher::parse)
|
||||
val skinUrl = obj.get("skinUrl")?.let(StringMatcher::parse)
|
||||
return SkullPredicate(profileId, textureProfileId, skinUrl, textureValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun test(stack: ItemStack, holder: LivingEntity?): Boolean {
|
||||
if (!stack.isOf(Items.PLAYER_HEAD)) return false
|
||||
val profile = stack.get(DataComponentTypes.PROFILE) ?: return false
|
||||
val textureProperty = profile.properties["textures"].firstOrNull()
|
||||
val textureMode = lazy(LazyThreadSafetyMode.NONE) {
|
||||
decodeProfileTextureProperty(textureProperty ?: return@lazy null)
|
||||
}
|
||||
when {
|
||||
profileId != null
|
||||
&& profileId != profile.id.getOrNull() ->
|
||||
return false
|
||||
|
||||
textureValue != null
|
||||
&& !textureValue.matches(textureProperty?.value ?: "") ->
|
||||
return false
|
||||
|
||||
skinUrl != null
|
||||
&& !skinUrl.matches(textureMode.value?.textures?.get(MinecraftProfileTexture.Type.SKIN)?.url ?: "") ->
|
||||
return false
|
||||
|
||||
textureProfileId != null
|
||||
&& textureProfileId != textureMode.value?.profileId ->
|
||||
return false
|
||||
|
||||
else -> return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,6 +139,26 @@ Filter by item type:
|
||||
"firmament:item": "minecraft:clock"
|
||||
```
|
||||
|
||||
#### Skulls
|
||||
|
||||
You can match skulls using the skull textures and other properties using the skull predicate. If there are no properties specified this is equivalent to checking if the item is a `minecraft:player_head`.
|
||||
|
||||
```json
|
||||
"firmament:skull": {
|
||||
"profileId": "cca2d452-c6d3-39cb-b695-5ec92b2d6729",
|
||||
"textureProfileId": "1d5233d388624bafb00e3150a7aa3a89",
|
||||
"skinUrl": "http://textures.minecraft.net/texture/7bf01c198f6e16965e230235cd22a5a9f4a40e40941234478948ff9a56e51775",
|
||||
"textureValue": "ewogICJ0aW1lc3RhbXAiIDogMTYxODUyMTY2MzY1NCwKICAicHJvZmlsZUlkIiA6ICIxZDUyMzNkMzg4NjI0YmFmYjAwZTMxNTBhN2FhM2E4OSIsCiAgInByb2ZpbGVOYW1lIiA6ICIwMDAwMDAwMDAwMDAwMDBKIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdiZjAxYzE5OGY2ZTE2OTY1ZTIzMDIzNWNkMjJhNWE5ZjRhNDBlNDA5NDEyMzQ0Nzg5NDhmZjlhNTZlNTE3NzUiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ"
|
||||
}
|
||||
```
|
||||
|
||||
| Name | Type | Description |
|
||||
|--------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `profileId` | UUID | Match the uuid of the profile component directly. |
|
||||
| `textureProfileId` | UUID | Match the uuid of the skin owner in the encoded texture value. This is more expensive, but can deviate from the profile id of the profile owner. |
|
||||
| `skinUrl` | [string](#string-matcher) | Match the texture url of the skin. This starts with `http://`, not with `https:/` in most cases. |
|
||||
| `textureValue` | [string](#string-matcher) | Match the texture value. This is the encoded base64 string of the texture url along with metadata. It is faster to query than the `skinUrl`, but it can out of changed without causing any semantic changes, and is less readable than the skinUrl. |
|
||||
|
||||
#### Extra attributes
|
||||
|
||||
Filter by extra attribute NBT data:
|
||||
|
||||
Reference in New Issue
Block a user