diff options
3 files changed, 80 insertions, 3 deletions
diff --git a/src/main/kotlin/util/Base64Util.kt b/src/main/kotlin/util/Base64Util.kt index 44bcdfd..c39c601 100644 --- a/src/main/kotlin/util/Base64Util.kt +++ b/src/main/kotlin/util/Base64Util.kt @@ -1,7 +1,14 @@ package moe.nea.firmament.util +import java.util.Base64 + object Base64Util { + fun decodeString(str: String): String { + return Base64.getDecoder().decode(str.padToValidBase64()) + .decodeToString() + } + fun String.padToValidBase64(): String { val align = this.length % 4 if (align == 0) return this diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ExtraAttributesPredicate.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ExtraAttributesPredicate.kt index 6fea1c3..bcf2eaf 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ExtraAttributesPredicate.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/predicates/ExtraAttributesPredicate.kt @@ -1,10 +1,12 @@ - package moe.nea.firmament.features.texturepack.predicates +import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import com.mojang.serialization.JsonOps +import kotlin.jvm.optionals.getOrDefault import moe.nea.firmament.features.texturepack.FirmamentModelPredicate import moe.nea.firmament.features.texturepack.FirmamentModelPredicateParser import moe.nea.firmament.features.texturepack.StringMatcher @@ -17,8 +19,10 @@ import net.minecraft.nbt.NbtFloat import net.minecraft.nbt.NbtInt import net.minecraft.nbt.NbtList import net.minecraft.nbt.NbtLong +import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtShort import net.minecraft.nbt.NbtString +import moe.nea.firmament.util.Base64Util import moe.nea.firmament.util.extraAttributes fun interface NbtMatcher { @@ -261,6 +265,20 @@ class NbtPrism(val path: List<String>) { var switch = mutableListOf<NbtElement>() for (pathSegment in path) { if (pathSegment == ".") continue + if (pathSegment != "*" && pathSegment.startsWith("*")) { + if (pathSegment == "*json") { + for (element in rootSet) { + val eString = element.asString() ?: continue + val element = Gson().fromJson(eString, JsonElement::class.java) + switch.add(JsonOps.INSTANCE.convertTo(NbtOps.INSTANCE, element)) + } + } else if (pathSegment == "*base64") { + for (element in rootSet) { + val string = element.asString() ?: continue + switch.add(NbtString.of(Base64Util.decodeString(string))) + } + } + } for (element in rootSet) { if (element is NbtList) { if (pathSegment == "*") diff --git a/web/src/pages/docs/_texture-pack-format.md b/web/src/pages/docs/_texture-pack-format.md index bfa22ff..fe6c36c 100644 --- a/web/src/pages/docs/_texture-pack-format.md +++ b/web/src/pages/docs/_texture-pack-format.md @@ -143,7 +143,7 @@ Filter by item type: Filter by extra attribute NBT data: -Specify a `path` to look at, separating sub elements with a `.`. You can use a `*` to check any child. +Specify a `path` (using an [nbt prism](#nbt-prism)) to look at, separating sub elements with a `.`. You can use a `*` to check any child. Then either specify a `match` sub-object or directly inline that object in the format of an [nbt matcher](#nbt-matcher). @@ -172,7 +172,7 @@ Sub object match: 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 +You can specify a `path` (using an [nbt prism](#nbt-prism)) 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). @@ -336,6 +336,58 @@ compare your number: This example would match if the level is less than fifty. The available operators are `<`, `>`, `<=` and `>=`. The operator needs to be specified on the left. The versions of the operator with `=` also allow the number to be equal. +### Nbt Prism + +An nbt prism (or path) is used to specify where in a complex nbt construct to look for a value. A basic prism just looks +like a dot-separated path (`parent.child.grandchild`), but more complex paths can be constructed. + +First the specified path is split into dot separated chunks: `"a.b.c"` -> `["a", "b", "c"]`. You can also directly +specify the list if you would like. Any entry in that list not starting with a `*` ist treated as an attribute name or +an index: + +```json +{ + "propA": { + "propB": { + "propC": 100, + "propD": 1000 + } + }, + "someOtherProp": "hello", + "someThirdProp": "{\"innerProp\": true}", + "someFourthProp": "aGlkZGVuIHZhbHVl" +} +``` + +In this example json (which is supposed to represent a corresponding nbt object), you can use a path like +`propA.propB.propC` to directly extract the value `100`. + +If you want to extract all of the innermost values of `propB` +(for example if `propB` was an array instead), you could use `propA.propB.*`. You can use the `*` at any level: +`*.*.*` for example extracts all properties that are exactly at the third level. In that case you would try to match any +of the values of `[100, 1000]` to your match object. + +Sometimes values are encoded in a non-nbt format inside a string. For those you can use other star based directives like +`*base64` or `*json` to decode those entries. + +`*base64` turns a base64 encoded string into the base64 decoded counterpart. `*.json` decodes a string into the json +object represented by that string. Note that json to nbt conversion isn't always straightforwards and the types can +end up being mangled (for example what could have been a byte ends up an int). + +| Path | Result | +|---------------------------------|---------------------------------| +| `propA.propB` | `{"propC": 100, "propD": 1000}` | +| `propA.propB.propC` | `100` | +| `propA.*.propC` | `100` | +| `propA.propB.*` | `100`, `1000` | +| `someOtherProp` | `"hello"` | +| `someThirdProp` | "{\"innerProp\": true}" | +| `someThirdProp.*json` | {"innerProp": true} | +| `someThirdProp.*json.innerProp` | true | +| `someFourthProp` | `"aGlkZGVuIHZhbHVl"` | +| `someFourthProp.*base64` | `"hidden value"` | + + ### Nbt Matcher This matches a single nbt element. |