diff options
author | Linnea Gräf <nea@nea.moe> | 2024-02-22 00:30:05 +0100 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-02-26 15:37:12 +0100 |
commit | 602112724d8236c1ec6671e1893128862c9f5815 (patch) | |
tree | fd0695839af0b2a8296e8d3009432644212638ce | |
parent | 2e571210c948d90a7d0ea8b07184102ceb401962 (diff) | |
download | firmament-602112724d8236c1ec6671e1893128862c9f5815.tar.gz firmament-602112724d8236c1ec6671e1893128862c9f5815.tar.bz2 firmament-602112724d8236c1ec6671e1893128862c9f5815.zip |
Add custom model predicates
Add regex support
Add and and or predicates
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()) + } +} |