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.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import moe.nea.firmament.util.parseDashlessUUID
|
import moe.nea.firmament.util.parseDashlessUUID
|
||||||
|
import moe.nea.firmament.util.parsePotentiallyDashlessUUID
|
||||||
|
|
||||||
object DashlessUUIDSerializer : KSerializer<UUID> {
|
object DashlessUUIDSerializer : KSerializer<UUID> {
|
||||||
override val descriptor: SerialDescriptor =
|
override val descriptor: SerialDescriptor =
|
||||||
@@ -17,10 +18,7 @@ object DashlessUUIDSerializer : KSerializer<UUID> {
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): UUID {
|
override fun deserialize(decoder: Decoder): UUID {
|
||||||
val str = decoder.decodeString()
|
val str = decoder.decodeString()
|
||||||
if ("-" in str) {
|
return parsePotentiallyDashlessUUID(str)
|
||||||
return UUID.fromString(str)
|
|
||||||
}
|
|
||||||
return parseDashlessUUID(str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: UUID) {
|
override fun serialize(encoder: Encoder, value: UUID) {
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ package moe.nea.firmament.util
|
|||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
fun parsePotentiallyDashlessUUID(unknownFormattedUUID: String): UUID {
|
||||||
|
if ("-" in unknownFormattedUUID)
|
||||||
|
return UUID.fromString(unknownFormattedUUID)
|
||||||
|
return parseDashlessUUID(unknownFormattedUUID)
|
||||||
|
}
|
||||||
|
|
||||||
fun parseDashlessUUID(dashlessUuid: String): UUID {
|
fun parseDashlessUUID(dashlessUuid: String): UUID {
|
||||||
val most = BigInteger(dashlessUuid.substring(0, 16), 16)
|
val most = BigInteger(dashlessUuid.substring(0, 16), 16)
|
||||||
val least = BigInteger(dashlessUuid.substring(16, 32), 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.OrPredicate
|
||||||
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
import moe.nea.firmament.features.texturepack.predicates.PetPredicate
|
||||||
import moe.nea.firmament.features.texturepack.predicates.PullingPredicate
|
import moe.nea.firmament.features.texturepack.predicates.PullingPredicate
|
||||||
|
import moe.nea.firmament.features.texturepack.predicates.SkullPredicate
|
||||||
import moe.nea.firmament.util.json.KJsonOps
|
import moe.nea.firmament.util.json.KJsonOps
|
||||||
|
|
||||||
object CustomModelOverrideParser {
|
object CustomModelOverrideParser {
|
||||||
@@ -65,6 +66,7 @@ object CustomModelOverrideParser {
|
|||||||
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
registerPredicateParser("extra_attributes", ExtraAttributesPredicate.Parser)
|
||||||
registerPredicateParser("pet", PetPredicate.Parser)
|
registerPredicateParser("pet", PetPredicate.Parser)
|
||||||
registerPredicateParser("component", GenericComponentPredicate.Parser)
|
registerPredicateParser("component", GenericComponentPredicate.Parser)
|
||||||
|
registerPredicateParser("skull", SkullPredicate.Parser)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val neverPredicate = listOf(
|
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"
|
"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
|
#### Extra attributes
|
||||||
|
|
||||||
Filter by extra attribute NBT data:
|
Filter by extra attribute NBT data:
|
||||||
|
|||||||
Reference in New Issue
Block a user