aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-02-22 00:30:05 +0100
committerLinnea Gräf <nea@nea.moe>2024-02-26 15:37:12 +0100
commit602112724d8236c1ec6671e1893128862c9f5815 (patch)
treefd0695839af0b2a8296e8d3009432644212638ce
parent2e571210c948d90a7d0ea8b07184102ceb401962 (diff)
downloadfirmament-602112724d8236c1ec6671e1893128862c9f5815.tar.gz
firmament-602112724d8236c1ec6671e1893128862c9f5815.tar.bz2
firmament-602112724d8236c1ec6671e1893128862c9f5815.zip
Add custom model predicates
Add regex support Add and and or predicates
-rw-r--r--build.gradle.kts25
-rw-r--r--src/main/java/moe/nea/firmament/mixins/custommodels/BakedOverrideDataHolder.java33
-rw-r--r--src/main/java/moe/nea/firmament/mixins/custommodels/ModelOverrideDataHolder.java33
-rw-r--r--src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java55
-rw-r--r--src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java52
-rw-r--r--src/main/kotlin/moe/nea/firmament/events/FeaturesInitializedEvent.kt13
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt3
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt12
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt31
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt13
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt48
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt2
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt28
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt13
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt13
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt28
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt12
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt24
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt31
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt70
-rw-r--r--src/main/kotlin/moe/nea/firmament/util/filter/IteratorFilterSet.kt38
-rw-r--r--src/main/kotlin/moe/nea/firmament/util/textutil.kt26
-rw-r--r--src/main/resources/firmament.accesswidener2
-rw-r--r--src/test/kotlin/moe/nea/firmament/test/ColorCode.kt28
24 files changed, 620 insertions, 13 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index 7662f06..38c1b9f 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,6 +1,10 @@
-// SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
-//
-// SPDX-License-Identifier: CC0-1.0
+/*
+ * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
import moe.nea.licenseextractificator.LicenseDiscoveryTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -149,8 +153,14 @@ dependencies {
transInclude.resolvedConfiguration.resolvedArtifacts.forEach {
include(it.moduleVersion.id.toString())
}
+
+
+ testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
}
+tasks.test {
+ useJUnitPlatform()
+}
version = rootProject.property("mod_version").toString()
group = rootProject.property("maven_group").toString()
@@ -187,11 +197,6 @@ tasks.jar {
tasks.shadowJar {
configurations = listOf(shadowMe)
archiveClassifier.set("dev")
- doLast {
- configurations.forEach {
- println("Copying files into jar: ${it.files}")
- }
- }
relocate("io.github.moulberry.repo", "moe.nea.firmament.deps.repo")
destinationDirectory.set(layout.buildDirectory.dir("badjars"))
}
@@ -205,10 +210,10 @@ tasks.remapJar {
tasks.processResources {
val replacements = listOf(
- "version" to project.version,
+ "version" to project.version.toString(),
"minecraft_version" to libs.versions.minecraft.get(),
"fabric_kotlin_version" to libs.versions.fabric.kotlin.get()
- ).map { (k, v) -> k to v.toString() }
+ )
replacements.forEach { (key, value) -> inputs.property(key, value) }
filesMatching("**/fabric.mod.json") {
expand(*replacements.toTypedArray())
diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/BakedOverrideDataHolder.java b/src/main/java/moe/nea/firmament/mixins/custommodels/BakedOverrideDataHolder.java
new file mode 100644
index 0000000..5e5f863
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/custommodels/BakedOverrideDataHolder.java
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.custommodels;
+
+import moe.nea.firmament.features.texturepack.BakedOverrideData;
+import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
+import net.minecraft.client.render.model.json.ModelOverrideList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+@Mixin(ModelOverrideList.BakedOverride.class)
+public class BakedOverrideDataHolder implements BakedOverrideData {
+
+ @Unique
+ private FirmamentModelPredicate[] firmamentOverrides;
+
+ @Nullable
+ @Override
+ public FirmamentModelPredicate[] getFirmamentOverrides() {
+ return firmamentOverrides;
+ }
+
+ @Override
+ public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
+ this.firmamentOverrides = overrides;
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/ModelOverrideDataHolder.java b/src/main/java/moe/nea/firmament/mixins/custommodels/ModelOverrideDataHolder.java
new file mode 100644
index 0000000..8a476db
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/custommodels/ModelOverrideDataHolder.java
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.custommodels;
+
+import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
+import moe.nea.firmament.features.texturepack.ModelOverrideData;
+import net.minecraft.client.render.model.json.ModelOverride;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+
+@Mixin(ModelOverride.class)
+public class ModelOverrideDataHolder implements ModelOverrideData {
+
+ @Unique
+ private FirmamentModelPredicate[] overrides;
+
+ @Nullable
+ @Override
+ public FirmamentModelPredicate[] getFirmamentOverrides() {
+ return overrides;
+ }
+
+ @Override
+ public void setFirmamentOverrides(@NotNull FirmamentModelPredicate[] overrides) {
+ this.overrides = overrides;
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java
new file mode 100644
index 0000000..e85bff0
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/custommodels/PatchOverrideDeserializer.java
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.custommodels;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.injector.ModifyReturnValue;
+import com.llamalad7.mixinextras.sugar.Local;
+import moe.nea.firmament.features.texturepack.CustomModelOverrideParser;
+import moe.nea.firmament.features.texturepack.ModelOverrideData;
+import net.minecraft.client.render.model.json.ModelOverride;
+import net.minecraft.util.Identifier;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.util.List;
+import java.util.Map;
+
+@Mixin(ModelOverride.Deserializer.class)
+public class PatchOverrideDeserializer {
+
+ @ModifyReturnValue(
+ method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/render/model/json/ModelOverride;",
+ at = @At(value = "RETURN"))
+ private ModelOverride addCustomOverrides(ModelOverride original, @Local JsonObject jsonObject) {
+ var originalData = (ModelOverrideData) original;
+ originalData.setFirmamentOverrides(CustomModelOverrideParser.parseCustomModelOverrides(jsonObject));
+ return original;
+ }
+
+ @ModifyExpressionValue(
+ method = "deserializeMinPropertyValues(Lcom/google/gson/JsonObject;)Ljava/util/List;",
+ at = @At(value = "INVOKE", target = "Ljava/util/Map$Entry;getValue()Ljava/lang/Object;"))
+ private Object removeFirmamentPredicatesFromJsonIteration(Object original, @Local Map.Entry<String, JsonElement> entry) {
+ if (entry.getKey().startsWith("firmament:")) return new JsonPrimitive(0F);
+ return original;
+ }
+
+ @Inject(
+ method = "deserializeMinPropertyValues",
+ at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;")
+ )
+ private void whatever(JsonObject object, CallbackInfoReturnable<List<ModelOverride.Condition>> cir,
+ @Local Map<Identifier, Float> maps) {
+ maps.entrySet().removeIf(it -> it.getKey().getNamespace().equals("firmament"));
+ }
+}
diff --git a/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java
new file mode 100644
index 0000000..4db9fc0
--- /dev/null
+++ b/src/main/java/moe/nea/firmament/mixins/custommodels/TestForFirmamentOverridePredicatesPatch.java
@@ -0,0 +1,52 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package moe.nea.firmament.mixins.custommodels;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.sugar.Local;
+import moe.nea.firmament.features.texturepack.BakedOverrideData;
+import moe.nea.firmament.features.texturepack.FirmamentModelPredicate;
+import moe.nea.firmament.features.texturepack.ModelOverrideData;
+import net.minecraft.client.render.model.json.ModelOverride;
+import net.minecraft.client.render.model.json.ModelOverrideList;
+import net.minecraft.item.ItemStack;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+
+@Mixin(ModelOverrideList.class)
+public class TestForFirmamentOverridePredicatesPatch {
+
+ @ModifyArg(method = "<init>(Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/json/JsonUnbakedModel;Ljava/util/List;)V",
+ at = @At(
+ value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"
+ ))
+ public Object onInit(
+ Object element,
+ @Local ModelOverride modelOverride
+ ) {
+ var bakedOverride = (ModelOverrideList.BakedOverride) element;
+ ((BakedOverrideData) bakedOverride)
+ .setFirmamentOverrides(((ModelOverrideData) modelOverride).getFirmamentOverrides());
+ return element;
+ }
+
+ @ModifyExpressionValue(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/json/ModelOverrideList$BakedOverride;test([F)Z"))
+ public boolean testFirmamentOverrides(boolean originalValue,
+ @Local ModelOverrideList.BakedOverride bakedOverride,
+ @Local ItemStack stack) {
+ if (!originalValue) return false;
+ var overrideData = (BakedOverrideData) bakedOverride;
+ var overrides = overrideData.getFirmamentOverrides();
+ if (overrides == null) return true;
+ for (FirmamentModelPredicate firmamentOverride : overrides) {
+ if (!firmamentOverride.test(stack))
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/events/FeaturesInitializedEvent.kt b/src/main/kotlin/moe/nea/firmament/events/FeaturesInitializedEvent.kt
new file mode 100644
index 0000000..da18568
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/events/FeaturesInitializedEvent.kt
@@ -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>()
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
index 99f84e6..5e7f612 100644
--- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt
@@ -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
}
}
diff --git a/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt b/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt
new file mode 100644
index 0000000..1f3a572
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/notifications/Notifications.kt
@@ -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 {
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt
new file mode 100644
index 0000000..5ad023e
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/AndPredicate.kt
@@ -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)
+ }
+
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt
new file mode 100644
index 0000000..8ed8402
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/BakedOverrideData.kt
@@ -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>?)
+
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt
new file mode 100644
index 0000000..ac62eaa
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomModelOverrideParser.kt
@@ -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()
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
index 64dec99..66c0036 100644
--- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomSkyBlockTextures.kt
@@ -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
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt
new file mode 100644
index 0000000..373910a
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/DisplayNamePredicate.kt
@@ -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))
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt
new file mode 100644
index 0000000..8dcdaf3
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicate.kt
@@ -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
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt
new file mode 100644
index 0000000..de7557a
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/FirmamentModelPredicateParser.kt
@@ -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
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt
new file mode 100644
index 0000000..604d29c
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/LorePredicate.kt
@@ -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)}
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt
new file mode 100644
index 0000000..ff68c94
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideData.kt
@@ -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>?)
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt
new file mode 100644
index 0000000..c8cacbd
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/ModelOverrideFilterSet.kt
@@ -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:")
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt
new file mode 100644
index 0000000..c171367
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/OrPredicate.kt
@@ -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)
+ }
+
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt
new file mode 100644
index 0000000..0fb8e00
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/StringMatcher.kt
@@ -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")
+ }
+ }
+}
diff --git a/src/main/kotlin/moe/nea/firmament/util/filter/IteratorFilterSet.kt b/src/main/kotlin/moe/nea/firmament/util/filter/IteratorFilterSet.kt
new file mode 100644
index 0000000..61d6524
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/util/filter/IteratorFilterSet.kt
@@ -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")
+ }
+ }
+ }
+}
+
diff --git a/src/main/kotlin/moe/nea/firmament/util/textutil.kt b/src/main/kotlin/moe/nea/firmament/util/textutil.kt
index f811bd8..1d61332 100644
--- a/src/main/kotlin/moe/nea/firmament/util/textutil.kt
+++ b/src/main/kotlin/moe/nea/firmament/util/textutil.kt
@@ -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 {
diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener
index 9f1b44b..92f4fe4 100644
--- a/src/main/resources/firmament.accesswidener
+++ b/src/main/resources/firmament.accesswidener
@@ -8,3 +8,5 @@ accessible method net/minecraft/client/render/model/ModelLoader$BakerImpl <init>
accessible field net/minecraft/client/network/ClientPlayNetworkHandler lastSeenMessagesCollector Lnet/minecraft/network/message/LastSeenMessagesCollector;
accessible field net/minecraft/client/gui/hud/InGameHud SCOREBOARD_ENTRY_COMPARATOR Ljava/util/Comparator;
+accessible class net/minecraft/client/render/model/json/ModelOverride$Deserializer
+accessible class net/minecraft/client/render/model/json/ModelOverrideList$BakedOverride
diff --git a/src/test/kotlin/moe/nea/firmament/test/ColorCode.kt b/src/test/kotlin/moe/nea/firmament/test/ColorCode.kt
new file mode 100644
index 0000000..737534e
--- /dev/null
+++ b/src/test/kotlin/moe/nea/firmament/test/ColorCode.kt
@@ -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.test
+
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import moe.nea.firmament.util.removeColorCodes
+
+
+class ColorCode {
+ @Test
+ fun testWhatever() {
+ Assertions.assertEquals("", "".removeColorCodes().toString())
+ Assertions.assertEquals("", "§".removeColorCodes().toString())
+ Assertions.assertEquals("", "§a".removeColorCodes().toString())
+ Assertions.assertEquals("ab", "a§ab".removeColorCodes().toString())
+ Assertions.assertEquals("ab", "a§ab§§".removeColorCodes().toString())
+ Assertions.assertEquals("abc", "a§ab§§c".removeColorCodes().toString())
+ Assertions.assertEquals("bc", "§ab§§c".removeColorCodes().toString())
+ Assertions.assertEquals("b§lc", "§ab§l§§c".removeColorCodes(true).toString())
+ Assertions.assertEquals("b§lc§l", "§ab§l§§c§l".removeColorCodes(true).toString())
+ Assertions.assertEquals("§lb§lc", "§l§ab§l§§c".removeColorCodes(true).toString())
+ }
+}