From 11f876ab571faddcacdecb6d8372b0ce91f15a34 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 6 Nov 2022 16:29:38 +0700 Subject: [PATCH] Slice and splice classes --- .../dbotthepony/mc/otm/item/MatterDustItem.kt | 7 +- .../mc/otm/matter/AbstractRegistryAction.kt | 148 ++++ .../dbotthepony/mc/otm/matter/DeleteAction.kt | 60 ++ .../dbotthepony/mc/otm/matter/IMatterItem.kt | 2 +- .../dbotthepony/mc/otm/matter/IMatterValue.kt | 43 ++ .../dbotthepony/mc/otm/matter/InsertAction.kt | 161 +++++ .../mc/otm/matter/MatterDataProvider.kt | 62 +- .../mc/otm/matter/MatterManager.kt | 643 ------------------ .../mc/otm/matter/RecipeResolverManager.kt | 16 +- .../dbotthepony/mc/otm/matter/UpdateAction.kt | 309 +++++++++ 10 files changed, 765 insertions(+), 686 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/matter/AbstractRegistryAction.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/matter/DeleteAction.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/matter/InsertAction.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/matter/UpdateAction.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/MatterDustItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/MatterDustItem.kt index 5ea54a84a..824b59b77 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/item/MatterDustItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/MatterDustItem.kt @@ -11,7 +11,8 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.matter.IMatterItem import ru.dbotthepony.mc.otm.core.set -import ru.dbotthepony.mc.otm.matter.MatterManager +import ru.dbotthepony.mc.otm.matter.IMatterValue +import ru.dbotthepony.mc.otm.matter.MatterValue class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB).stacksTo(64)), IMatterItem { private fun matter(stack: ItemStack): ImpreciseFraction { @@ -22,9 +23,9 @@ class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREAT stack.orCreateTag["matter"] = matter.serializeNBT() } - override fun getMatterValue(stack: ItemStack): MatterManager.IMatterValue? { + override fun getMatterValue(stack: ItemStack): IMatterValue? { val value = stack.tag?.get("matter") ?: return null - return MatterManager.MatterValue(ImpreciseFraction.deserializeNBT(value), 0.0) + return MatterValue(ImpreciseFraction.deserializeNBT(value), 0.0) } override fun canDecompose(stack: ItemStack) = false diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/AbstractRegistryAction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/AbstractRegistryAction.kt new file mode 100644 index 000000000..ca75dc60d --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/AbstractRegistryAction.kt @@ -0,0 +1,148 @@ +package ru.dbotthepony.mc.otm.matter + +import com.google.gson.JsonObject +import com.google.gson.JsonParseException +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSyntaxException +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.ItemTags +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.set + +sealed class AbstractRegistryAction { + sealed class Condition { + abstract fun test(): Boolean + } + + class CombinedCondition(val conditions: Collection) : Condition() { + override fun test(): Boolean { + return conditions.all { it.test() } + } + } + + val errorOnFailure: Boolean + val tag: TagKey? + val key: ResourceLocation? + val matter: ImpreciseFraction? + val complexity: Double? + val priority: Int? + + constructor(json: JsonObject) { + errorOnFailure = json["error_on_failure"]?.asBoolean ?: false + priority = json["priority"]?.asInt + + val id = json["id"]?.asString ?: throw JsonParseException("Missing `id` value") + + if (id.startsWith("#")) { + if (id.startsWith("#:")) { + throw JsonSyntaxException("Invalid `id` value: $id") + } + + tag = ItemTags.create( + ResourceLocation.tryParse(id.substring(1)) ?: throw JsonSyntaxException("Invalid `id` value: $id") + ) + key = null + } else { + key = ResourceLocation.tryParse(id) ?: throw JsonSyntaxException("Invalid `id` value: $id") + tag = null + } + + try { + matter = json["matter"]?.asString?.let(::ImpreciseFraction) + + if (matter != null && !matter.isPositive) { + throw JsonParseException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") + } + } catch(err: NumberFormatException) { + throw JsonParseException("Invalid `matter` field: ${json["matter"]}", err) + } + + try { + complexity = json["complexity"]?.asString?.toDouble() + + if (complexity != null && complexity <= 0.0) { + throw JsonParseException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") + } + } catch(err: NumberFormatException) { + throw JsonParseException("Invalid `complexity` field: ${json["complexity"]}", err) + } + } + + constructor( + tag: TagKey, + matter: ImpreciseFraction?, + complexity: Double?, + priority: Int? = null, + errorOnFailure: Boolean = false + ) { + this.tag = tag + this.key = null + this.matter = matter + this.complexity = complexity + this.priority = priority + this.errorOnFailure = errorOnFailure + + if (matter != null && !matter.isPositive) { + throw IllegalArgumentException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") + } + + if (complexity != null && complexity <= 0.0) { + throw IllegalArgumentException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") + } + } + + constructor( + key: ResourceLocation, + matter: ImpreciseFraction?, + complexity: Double?, + priority: Int? = null, + errorOnFailure: Boolean = false + ) { + this.tag = null + this.key = key + this.matter = matter + this.complexity = complexity + this.priority = priority + this.errorOnFailure = errorOnFailure + + if (matter != null && !matter.isPositive) { + throw IllegalArgumentException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") + } + + if (complexity != null && complexity <= 0.0) { + throw IllegalArgumentException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") + } + } + + fun checkConditions(): Boolean { + return true + } + + abstract fun update(registry: MutableCollection, modifier: ResourceLocation) + + protected inline fun fail(reason: () -> String) { + if (errorOnFailure) { + throw JsonParseException(reason.invoke()) + } + } + + open fun toJson(): JsonObject { + return JsonObject().also { + if (key != null) + it["id"] = JsonPrimitive(key.toString()) + else + it["id"] = JsonPrimitive("#${tag!!.location}") + + if (priority != null) + it["priority"] = priority + + if (matter != null) + it["matter"] = matter.toString() + + if (complexity != null) + it["complexity"] = complexity + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/DeleteAction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/DeleteAction.kt new file mode 100644 index 000000000..612d0fe27 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/DeleteAction.kt @@ -0,0 +1,60 @@ +package ru.dbotthepony.mc.otm.matter + +import com.google.gson.JsonObject +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import ru.dbotthepony.mc.otm.core.set + +class DeleteAction : AbstractRegistryAction { + constructor( + tag: TagKey, + errorOnFailure: Boolean = false, + ) : super(tag = tag, errorOnFailure = errorOnFailure, matter = null, complexity = null) + + constructor( + key: ResourceLocation, + errorOnFailure: Boolean = false, + ) : super(key = key, errorOnFailure = errorOnFailure, matter = null, complexity = null) + + constructor(json: JsonObject) : super(json) + + override fun toJson(): JsonObject { + return super.toJson().also { + it["action"] = "delete" + } + } + + override fun update(registry: MutableCollection, modifier: ResourceLocation) { + if (!checkConditions()) { + return + } + + check(matter == null) { "Delete action can't have matter value (for $modifier)" } + check(complexity == null) { "Delete action can't have complexity value (for $modifier)" } + + if (key != null) { + val iterator = registry.iterator() + + for (value in iterator) { + if (value is MatterManager.MutableKeyEntry && value.key == key) { + iterator.remove() + return + } + } + + fail { "Could not find matter value with key $key" } + } else { + val iterator = registry.iterator() + + for (value in iterator) { + if (value is MatterManager.MutableTagEntry && value.tag == tag) { + iterator.remove() + return + } + } + + fail { "Could not find matter value with tag $tag" } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterItem.kt index 4b434a962..5e565891d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterItem.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterItem.kt @@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.matter import net.minecraft.world.item.ItemStack interface IMatterItem { - fun getMatterValue(stack: ItemStack): MatterManager.IMatterValue? + fun getMatterValue(stack: ItemStack): IMatterValue? fun hasMatterValue(stack: ItemStack) = getMatterValue(stack) != null fun canDecompose(stack: ItemStack): Boolean } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt new file mode 100644 index 000000000..4eed564c3 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/IMatterValue.kt @@ -0,0 +1,43 @@ +package ru.dbotthepony.mc.otm.matter + +import ru.dbotthepony.mc.otm.core.ImpreciseFraction + +interface IMatterValue : Comparable { + val matter: ImpreciseFraction + val complexity: Double + + val hasMatterValue: Boolean get() = matter.isPositive && complexity > 0.0 + + operator fun plus(other: IMatterValue): IMatterValue { + return MatterValue(matter + other.matter, complexity + other.complexity) + } + + operator fun minus(other: IMatterValue): IMatterValue { + return MatterValue(matter - other.matter, complexity - other.complexity) + } + + override fun compareTo(other: IMatterValue): Int { + val matterComparison = matter.compareTo(other.matter) + + if (matterComparison == 0) { + return complexity.compareTo(other.complexity) + } + + return matterComparison + } + + /** + * ZERO + */ + companion object : IMatterValue { + override val matter: ImpreciseFraction + get() = ImpreciseFraction.ZERO + override val complexity: Double + get() = 0.0 + } +} + +data class MatterValue( + override val matter: ImpreciseFraction, + override val complexity: Double +) : IMatterValue diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/InsertAction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/InsertAction.kt new file mode 100644 index 000000000..1311d6102 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/InsertAction.kt @@ -0,0 +1,161 @@ +package ru.dbotthepony.mc.otm.matter + +import com.google.gson.JsonObject +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.probablyParallelStream +import ru.dbotthepony.mc.otm.core.set + +class InsertAction : AbstractRegistryAction { + val replaceIfExists: Boolean + val comparePriority: Boolean + + constructor( + key: ResourceLocation, + matter: ImpreciseFraction?, + complexity: Double?, + priority: Int? = null, + errorOnFailure: Boolean = false, + replaceIfExists: Boolean = false, + comparePriority: Boolean = true, + ) : super(key = key, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { + this.replaceIfExists = replaceIfExists + this.comparePriority = comparePriority + } + + constructor( + tag: TagKey, + matter: ImpreciseFraction?, + complexity: Double?, + priority: Int? = null, + errorOnFailure: Boolean = false, + replaceIfExists: Boolean = false, + comparePriority: Boolean = true, + ) : super(tag = tag, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { + this.replaceIfExists = replaceIfExists + this.comparePriority = comparePriority + } + + constructor(json: JsonObject) : super(json) { + this.replaceIfExists = json["replace_if_exists"]?.asBoolean ?: false + this.comparePriority = json["compare_priority"]?.asBoolean ?: true + } + + override fun toJson(): JsonObject { + checkNotNull(matter) { "Missing matter value, which is required for insert action" } + checkNotNull(complexity) { "Missing complexity value, which is required for insert action" } + + return super.toJson().also { + it["action"] = "insert" + it["replace_if_exists"] = replaceIfExists + it["compare_priority"] = comparePriority + } + } + + override fun update(registry: MutableCollection, modifier: ResourceLocation) { + checkNotNull(matter) { "Missing matter value, which is required for insert action (for $modifier)" } + checkNotNull(complexity) { "Missing complexity value, which is required for insert action (for $modifier)" } + + check(!comparePriority || priority != null) { "If compare_priority is true then priority must not be null (for $modifier)" } + + if (!checkConditions()) { + return + } + + if (key != null) { + if (replaceIfExists) { + // search & replace in parallel, if possible + val replaced = registry.probablyParallelStream().anyMatch { + if (it is MatterManager.MutableKeyEntry && it.key == key) { + if (!comparePriority || it.priority < priority!!) { + it.matter = matter + it.complexity = complexity + it.priority = priority ?: 0 + it.modificationChain.clear() + it.modificationChain.add(modifier) + } + + return@anyMatch true + } else { + return@anyMatch false + } + } + + if (!replaced) { + registry.add( + MatterManager.MutableKeyEntry( + key, + mutableListOf(modifier), + matter, + complexity, + priority ?: 0 + ) + ) + } + } else { + if (registry.probablyParallelStream().anyMatch { it is MatterManager.MutableKeyEntry && it.key == key }) { + fail { "Value with key $key already exists" } + return + } + + registry.add( + MatterManager.MutableKeyEntry( + key, + mutableListOf(modifier), + matter, + complexity, + priority ?: 0 + ) + ) + } + } else { + if (replaceIfExists) { + // search & replace in parallel, if possible + val replaced = registry.probablyParallelStream().anyMatch { + if (it is MatterManager.MutableTagEntry && it.tag == tag) { + if (!comparePriority || it.priority < priority!!) { + it.matter = matter + it.complexity = complexity + it.priority = priority ?: 0 + it.modificationChain.clear() + it.modificationChain.add(modifier) + } + + return@anyMatch true + } else { + return@anyMatch false + } + } + + if (!replaced) { + registry.add( + MatterManager.MutableTagEntry( + tag!!, + mutableListOf(modifier), + matter, + complexity, + priority ?: 0 + ) + ) + } + } else { + if (registry.probablyParallelStream().anyMatch { it is MatterManager.MutableTagEntry && it.tag == tag }) { + fail { "Value with tag $tag already exists" } + return + } + + registry.add( + MatterManager.MutableTagEntry( + tag!!, + mutableListOf(modifier), + matter, + complexity, + priority ?: 0 + ) + ) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterDataProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterDataProvider.kt index 2e4cd223e..7df5ac405 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterDataProvider.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterDataProvider.kt @@ -18,7 +18,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na constructor(event: GatherDataEvent) : this(event.generator, event.modContainer.namespace) protected val pathProvider: DataGenerator.PathProvider = dataGenerator.createPathProvider(DataGenerator.Target.DATA_PACK, MatterManager.DIRECTORY) - protected val actions = LinkedHashMap() + protected val actions = LinkedHashMap() sealed class Configuration(val name: ResourceLocation) { var errorOnFailure: Boolean = false @@ -54,20 +54,20 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na check(complexity == null || (complexity ?: throw ConcurrentModificationException()) >= 0.0) { "Invalid matter value: ${matter ?: throw ConcurrentModificationException()}" } } - abstract fun build(): MatterManager.AbstractRegistryAction + abstract fun build(): AbstractRegistryAction } class InsertConfiguration(name: ResourceLocation) : Configuration(name) { var replaceIfExists: Boolean = false var comparePriority: Boolean = false - override fun build(): MatterManager.InsertAction { + override fun build(): InsertAction { checkKeyTag() checkMatterValues() check(matter != null && complexity != null) { "You must define both matter value and complexity for $name" } if (key != null) { - return MatterManager.InsertAction( + return InsertAction( key = key ?: throw ConcurrentModificationException(), matter = matter, complexity = complexity, @@ -77,7 +77,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na comparePriority = comparePriority ) } else { - return MatterManager.InsertAction( + return InsertAction( tag = tag ?: throw ConcurrentModificationException(), matter = matter, complexity = complexity, @@ -91,19 +91,19 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na } class DeleteConfiguration(name: ResourceLocation) : Configuration(name) { - override fun build(): MatterManager.DeleteAction { + override fun build(): DeleteAction { checkKeyTag() check(matter == null) { "Delete action can't have matter value" } check(complexity == null) { "Delete action can't have complexity value" } if (key != null) { - return MatterManager.DeleteAction( + return DeleteAction( key = key ?: throw ConcurrentModificationException(), errorOnFailure = errorOnFailure, ) } else { - return MatterManager.DeleteAction( + return DeleteAction( tag = tag ?: throw ConcurrentModificationException(), errorOnFailure = errorOnFailure, ) @@ -112,29 +112,29 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na } class UpdateConfiguration(name: ResourceLocation) : Configuration(name) { - val matterFunctions = ArrayList>() - val complexityFunctions = ArrayList>() - val priorityFunctions = ArrayList>() + val matterFunctions = ArrayList>() + val complexityFunctions = ArrayList>() + val priorityFunctions = ArrayList>() - fun addMatterFunction(function: MatterManager.UpdateAction.Function, value: ImpreciseFraction): UpdateConfiguration { - matterFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) + fun addMatterFunction(function: UpdateAction.Function, value: ImpreciseFraction): UpdateConfiguration { + matterFunctions.add(UpdateAction.BoundFunction(function, value)) matter = null return this } - fun addComplexityFunction(function: MatterManager.UpdateAction.Function, value: Double): UpdateConfiguration { - complexityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) + fun addComplexityFunction(function: UpdateAction.Function, value: Double): UpdateConfiguration { + complexityFunctions.add(UpdateAction.BoundFunction(function, value)) matter = null return this } - fun addPriorityFunction(function: MatterManager.UpdateAction.Function, value: Int): UpdateConfiguration { - priorityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) + fun addPriorityFunction(function: UpdateAction.Function, value: Int): UpdateConfiguration { + priorityFunctions.add(UpdateAction.BoundFunction(function, value)) matter = null return this } - override fun build(): MatterManager.UpdateAction { + override fun build(): UpdateAction { check(!(matterFunctions.isNotEmpty() && matter != null)) { "Can't have both matter value and matter value functions be defined at the same time" } check(!(complexityFunctions.isNotEmpty() && complexity != null)) { "Can't have both complexity and complexity functions be defined at the same time" } check(!(priorityFunctions.isNotEmpty() && priority != null)) { "Can't have both priority and priority functions be defined at the same time" } @@ -149,7 +149,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na checkKeyTag() if (key != null) { - return MatterManager.UpdateAction( + return UpdateAction( key = key ?: throw ConcurrentModificationException(), matter = matter, complexity = complexity, @@ -160,7 +160,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na priorityFunctions = priorityFunctions ) } else { - return MatterManager.UpdateAction( + return UpdateAction( tag = tag ?: throw ConcurrentModificationException(), matter = matter, complexity = complexity, @@ -735,7 +735,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na } } - fun insert(name: ResourceLocation, configurator: InsertConfiguration.() -> Unit): MatterManager.InsertAction { + fun insert(name: ResourceLocation, configurator: InsertConfiguration.() -> Unit): InsertAction { check(!actions.containsKey(name)) { "Already has action with name $name" } val config = InsertConfiguration(name) configurator.invoke(config) @@ -744,7 +744,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na return built } - fun delete(name: ResourceLocation, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction { + fun delete(name: ResourceLocation, configurator: DeleteConfiguration.() -> Unit): DeleteAction { check(!actions.containsKey(name)) { "Already has action with name $name" } val config = DeleteConfiguration(name) configurator.invoke(config) @@ -753,7 +753,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na return built } - fun update(name: ResourceLocation, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction { + fun update(name: ResourceLocation, configurator: UpdateConfiguration.() -> Unit): UpdateAction { check(!actions.containsKey(name)) { "Already has action with name $name" } val config = UpdateConfiguration(name) configurator.invoke(config) @@ -774,42 +774,42 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na } } - fun insert(name: ItemLike, configurator: InsertConfiguration.() -> Unit): MatterManager.InsertAction { + fun insert(name: ItemLike, configurator: InsertConfiguration.() -> Unit): InsertAction { return insert(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) { key = name.asItem().registryName ?: throw ConcurrentModificationException() configurator.invoke(this) } } - fun delete(name: ItemLike, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction { + fun delete(name: ItemLike, configurator: DeleteConfiguration.() -> Unit): DeleteAction { return delete(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) { key = name.asItem().registryName ?: throw ConcurrentModificationException() configurator.invoke(this) } } - fun update(name: ItemLike, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction { + fun update(name: ItemLike, configurator: UpdateConfiguration.() -> Unit): UpdateAction { return update(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) { key = name.asItem().registryName ?: throw ConcurrentModificationException() configurator.invoke(this) } } - fun insert(name: TagKey, configurator: InsertConfiguration.() -> Unit): MatterManager.InsertAction { + fun insert(name: TagKey, configurator: InsertConfiguration.() -> Unit): InsertAction { return insert(updateLocation(name.location, "tag/")) { tag = name configurator.invoke(this) } } - fun delete(name: TagKey, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction { + fun delete(name: TagKey, configurator: DeleteConfiguration.() -> Unit): DeleteAction { return delete(updateLocation(name.location, "tag/")) { tag = name configurator.invoke(this) } } - fun update(name: TagKey, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction { + fun update(name: TagKey, configurator: UpdateConfiguration.() -> Unit): UpdateAction { return update(updateLocation(name.location, "tag/")) { tag = name configurator.invoke(this) @@ -886,8 +886,8 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na */ protected open fun addActions() {} - protected val added = ArrayList() - val addedView: List = Collections.unmodifiableList(added) + protected val added = ArrayList() + val addedView: List = Collections.unmodifiableList(added) final override fun run(output: CachedOutput) { addActions() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt index 187b26329..799528e71 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt @@ -2,21 +2,16 @@ package ru.dbotthepony.mc.otm.matter import com.google.common.collect.ImmutableList import com.google.gson.GsonBuilder -import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonParseException -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSyntaxException import com.mojang.blaze3d.platform.InputConstants import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import net.minecraft.ChatFormatting -import net.minecraft.client.Minecraft import net.minecraft.resources.ResourceLocation import net.minecraft.server.packs.resources.ResourceManager import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener -import net.minecraft.tags.ItemTags import net.minecraft.tags.TagKey import net.minecraft.util.profiling.ProfilerFiller import net.minecraft.world.item.Item @@ -36,14 +31,9 @@ import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.formatMatterFull import ru.dbotthepony.mc.otm.core.formatSiComponent -import ru.dbotthepony.mc.otm.core.integerDivisionDown -import ru.dbotthepony.mc.otm.core.integerDivisionUp import ru.dbotthepony.mc.otm.core.isZero import ru.dbotthepony.mc.otm.core.orNull -import ru.dbotthepony.mc.otm.core.probablyParallelStream import ru.dbotthepony.mc.otm.core.registryName -import ru.dbotthepony.mc.otm.core.set -import ru.dbotthepony.mc.otm.data.stream import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import java.math.BigInteger import java.util.Collections @@ -53,599 +43,6 @@ import kotlin.math.pow object MatterManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_matter") { const val DIRECTORY = "otm_matter" - sealed class AbstractRegistryAction { - sealed class Condition { - abstract fun test(): Boolean - } - - class CombinedCondition(val conditions: Collection) : Condition() { - override fun test(): Boolean { - return conditions.all { it.test() } - } - } - - val errorOnFailure: Boolean - val tag: TagKey? - val key: ResourceLocation? - val matter: ImpreciseFraction? - val complexity: Double? - val priority: Int? - - constructor(json: JsonObject) { - errorOnFailure = json["error_on_failure"]?.asBoolean ?: false - priority = json["priority"]?.asInt - - val id = json["id"]?.asString ?: throw JsonParseException("Missing `id` value") - - if (id.startsWith("#")) { - if (id.startsWith("#:")) { - throw JsonSyntaxException("Invalid `id` value: $id") - } - - tag = ItemTags.create(ResourceLocation.tryParse(id.substring(1)) ?: throw JsonSyntaxException("Invalid `id` value: $id")) - key = null - } else { - key = ResourceLocation.tryParse(id) ?: throw JsonSyntaxException("Invalid `id` value: $id") - tag = null - } - - try { - matter = json["matter"]?.asString?.let(::ImpreciseFraction) - - if (matter != null && !matter.isPositive) { - throw JsonParseException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") - } - } catch(err: NumberFormatException) { - throw JsonParseException("Invalid `matter` field: ${json["matter"]}", err) - } - - try { - complexity = json["complexity"]?.asString?.toDouble() - - if (complexity != null && complexity <= 0.0) { - throw JsonParseException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") - } - } catch(err: NumberFormatException) { - throw JsonParseException("Invalid `complexity` field: ${json["complexity"]}", err) - } - } - - constructor( - tag: TagKey, - matter: ImpreciseFraction?, - complexity: Double?, - priority: Int? = null, - errorOnFailure: Boolean = false - ) { - this.tag = tag - this.key = null - this.matter = matter - this.complexity = complexity - this.priority = priority - this.errorOnFailure = errorOnFailure - - if (matter != null && !matter.isPositive) { - throw IllegalArgumentException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") - } - - if (complexity != null && complexity <= 0.0) { - throw IllegalArgumentException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") - } - } - - constructor( - key: ResourceLocation, - matter: ImpreciseFraction?, - complexity: Double?, - priority: Int? = null, - errorOnFailure: Boolean = false - ) { - this.tag = null - this.key = key - this.matter = matter - this.complexity = complexity - this.priority = priority - this.errorOnFailure = errorOnFailure - - if (matter != null && !matter.isPositive) { - throw IllegalArgumentException("Can't have non-positive matter value. To remove an entry from registry please use 'delete' action instead.") - } - - if (complexity != null && complexity <= 0.0) { - throw IllegalArgumentException("Can't have non-positive complexity. To remove an entry from registry please use 'delete' action instead.") - } - } - - fun checkConditions(): Boolean { - return true - } - - abstract fun update(registry: MutableCollection, modifier: ResourceLocation) - - protected inline fun fail(reason: () -> String) { - if (errorOnFailure) { - throw JsonParseException(reason.invoke()) - } - } - - open fun toJson(): JsonObject { - return JsonObject().also { - if (key != null) - it["id"] = JsonPrimitive(key.toString()) - else - it["id"] = JsonPrimitive("#${tag!!.location}") - - if (priority != null) - it["priority"] = priority - - if (matter != null) - it["matter"] = matter.toString() - - if (complexity != null) - it["complexity"] = complexity - } - } - } - - class InsertAction : AbstractRegistryAction { - val replaceIfExists: Boolean - val comparePriority: Boolean - - constructor( - key: ResourceLocation, - matter: ImpreciseFraction?, - complexity: Double?, - priority: Int? = null, - errorOnFailure: Boolean = false, - replaceIfExists: Boolean = false, - comparePriority: Boolean = true, - ) : super(key = key, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { - this.replaceIfExists = replaceIfExists - this.comparePriority = comparePriority - } - - constructor( - tag: TagKey, - matter: ImpreciseFraction?, - complexity: Double?, - priority: Int? = null, - errorOnFailure: Boolean = false, - replaceIfExists: Boolean = false, - comparePriority: Boolean = true, - ) : super(tag = tag, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { - this.replaceIfExists = replaceIfExists - this.comparePriority = comparePriority - } - - constructor(json: JsonObject) : super(json) { - this.replaceIfExists = json["replace_if_exists"]?.asBoolean ?: false - this.comparePriority = json["compare_priority"]?.asBoolean ?: true - } - - override fun toJson(): JsonObject { - checkNotNull(matter) { "Missing matter value, which is required for insert action" } - checkNotNull(complexity) { "Missing complexity value, which is required for insert action" } - - return super.toJson().also { - it["action"] = "insert" - it["replace_if_exists"] = replaceIfExists - it["compare_priority"] = comparePriority - } - } - - override fun update(registry: MutableCollection, modifier: ResourceLocation) { - checkNotNull(matter) { "Missing matter value, which is required for insert action (for $modifier)" } - checkNotNull(complexity) { "Missing complexity value, which is required for insert action (for $modifier)" } - - check(!comparePriority || priority != null) { "If compare_priority is true then priority must not be null (for $modifier)" } - - if (!checkConditions()) { - return - } - - if (key != null) { - if (replaceIfExists) { - // search & replace in parallel, if possible - val replaced = registry.probablyParallelStream().anyMatch { - if (it is MutableKeyEntry && it.key == key) { - if (!comparePriority || it.priority < priority!!) { - it.matter = matter - it.complexity = complexity - it.priority = priority ?: 0 - it.modificationChain.clear() - it.modificationChain.add(modifier) - } - - return@anyMatch true - } else { - return@anyMatch false - } - } - - if (!replaced) { - registry.add(MutableKeyEntry(key, mutableListOf(modifier), matter, complexity, priority ?: 0)) - } - } else { - if (registry.probablyParallelStream().anyMatch { it is MutableKeyEntry && it.key == key }) { - fail { "Value with key $key already exists" } - return - } - - registry.add(MutableKeyEntry(key, mutableListOf(modifier), matter, complexity, priority ?: 0)) - } - } else { - if (replaceIfExists) { - // search & replace in parallel, if possible - val replaced = registry.probablyParallelStream().anyMatch { - if (it is MutableTagEntry && it.tag == tag) { - if (!comparePriority || it.priority < priority!!) { - it.matter = matter - it.complexity = complexity - it.priority = priority ?: 0 - it.modificationChain.clear() - it.modificationChain.add(modifier) - } - - return@anyMatch true - } else { - return@anyMatch false - } - } - - if (!replaced) { - registry.add(MutableTagEntry(tag!!, mutableListOf(modifier), matter, complexity, priority ?: 0)) - } - } else { - if (registry.probablyParallelStream().anyMatch { it is MutableTagEntry && it.tag == tag }) { - fail { "Value with tag $tag already exists" } - return - } - - registry.add(MutableTagEntry(tag!!, mutableListOf(modifier), matter, complexity, priority ?: 0)) - } - } - } - } - - class DeleteAction : AbstractRegistryAction { - constructor( - tag: TagKey, - errorOnFailure: Boolean = false, - ) : super(tag = tag, errorOnFailure = errorOnFailure, matter = null, complexity = null) - - constructor( - key: ResourceLocation, - errorOnFailure: Boolean = false, - ) : super(key = key, errorOnFailure = errorOnFailure, matter = null, complexity = null) - - constructor(json: JsonObject) : super(json) - - override fun toJson(): JsonObject { - return super.toJson().also { - it["action"] = "delete" - } - } - - override fun update(registry: MutableCollection, modifier: ResourceLocation) { - if (!checkConditions()) { - return - } - - check(matter == null) { "Delete action can't have matter value (for $modifier)" } - check(complexity == null) { "Delete action can't have complexity value (for $modifier)" } - - if (key != null) { - val iterator = registry.iterator() - - for (value in iterator) { - if (value is MutableKeyEntry && value.key == key) { - iterator.remove() - return - } - } - - fail { "Could not find matter value with key $key" } - } else { - val iterator = registry.iterator() - - for (value in iterator) { - if (value is MutableTagEntry && value.tag == tag) { - iterator.remove() - return - } - } - - fail { "Could not find matter value with tag $tag" } - } - } - } - - class UpdateAction : AbstractRegistryAction { - enum class Function { - ADD { - override fun updateValue(self: Int, other: Int): Int = self + other - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self + other - override fun updateValue(self: Double, other: Double): Double = self + other - }, - SUBTRACT { - override fun updateValue(self: Int, other: Int): Int = self - other - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self - other - override fun updateValue(self: Double, other: Double): Double = self - other - }, - MULTIPLY { - override fun updateValue(self: Int, other: Int): Int = self * other - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self * other - override fun updateValue(self: Double, other: Double): Double = self * other - }, - DIVIDE { - override fun updateValue(self: Int, other: Int): Int = self / other - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self / other - override fun updateValue(self: Double, other: Double): Double = self / other - }, - DIVIDE_UP { - override fun updateValue(self: Int, other: Int): Int = integerDivisionUp(self, other) - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = throw JsonParseException("Integer division up is available only for integers") - override fun updateValue(self: Double, other: Double): Double = throw JsonParseException("Integer division up is available only for integers") - }, - DIVIDE_DOWN { - override fun updateValue(self: Int, other: Int): Int = integerDivisionDown(self, other) - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = throw JsonParseException("Integer division down is available only for integers") - override fun updateValue(self: Double, other: Double): Double = throw JsonParseException("Integer division down is available only for integers") - }, - MODULO { - override fun updateValue(self: Int, other: Int): Int = self % other - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self % other - override fun updateValue(self: Double, other: Double): Double = self % other - }, - AT_LEAST { - override fun updateValue(self: Int, other: Int): Int = self.coerceAtLeast(other) - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self.coerceAtLeast(other) - override fun updateValue(self: Double, other: Double): Double = self.coerceAtLeast(other) - }, - AT_MOST { - override fun updateValue(self: Int, other: Int): Int = self.coerceAtMost(other) - override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self.coerceAtMost(other) - override fun updateValue(self: Double, other: Double): Double = self.coerceAtMost(other) - }, - ; - - protected abstract fun updateValue(self: Int, other: Int): Int - protected abstract fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction - protected abstract fun updateValue(self: Double, other: Double): Double - - fun updateValue(self: T, other: T): T { - return when (self) { - is ImpreciseFraction -> updateValue(self, other) - is Double -> updateValue(self, other) - is Int -> updateValue(self, other) - else -> throw RuntimeException("Unknown number type: ${self::class.qualifiedName}") - } - } - } - - class BoundFunction(val function: Function, val value: T) { - fun apply(self: T): T { - return function.updateValue(self, value) - } - - fun toJson(): JsonObject { - return JsonObject().also { - it["type"] = function.name - - if (value is Int || value is Double) - it["value"] = value - else - it["value"] = value.toString() - } - } - } - - companion object { - inline fun getValue(value: JsonElement): T { - return when (T::class) { - ImpreciseFraction::class -> ImpreciseFraction(value.asString) as T - Double::class -> value.asDouble as T - Int::class -> value.asInt as T - else -> throw RuntimeException("Unknown number type: ${T::class.qualifiedName}") - } - } - - inline fun deserializeFunctionTree(input: JsonElement?, name: String): List> { - if (input == null) { - return listOf() - } - - if (input !is JsonArray) { - throw JsonParseException("Expected $name to be JsonArray, ${input::class.qualifiedName} given") - } - - return input.stream().map { - if (it !is JsonObject) { - throw JsonParseException("All elements in function tree must be JsonObjects, ${it::class.qualifiedName} given") - } - - val type = it["type"]?.asString ?: throw JsonParseException("Invalid `type` value in function object") - val value = it["value"] ?: throw JsonParseException("Missing `value` value in function object") - - val parsedValue: T = getValue(value) - val fn: Function - - try { - fn = Function.valueOf(type.uppercase()) - } catch (err: NoSuchElementException) { - throw JsonParseException("No such function: $type", err) - } - - return@map BoundFunction(fn, parsedValue) - }.collect(ImmutableList.toImmutableList()) - } - - fun apply(value: T, funcs: Collection>): T { - if (funcs.isEmpty()) { - return value - } - - var newValue = value - - for (func in funcs) { - newValue = func.apply(newValue) - } - - return newValue - } - } - - val matterFunctions: List> - val complexityFunctions: List> - val priorityFunctions: List> - - constructor(json: JsonObject) : super(json) { - matterFunctions = deserializeFunctionTree(json["matter_functions"], "matter_functions") - complexityFunctions = deserializeFunctionTree(json["complexity_functions"], "complexity_functions") - priorityFunctions = deserializeFunctionTree(json["priority_functions"], "priority_functions") - } - - constructor( - tag: TagKey, - matter: ImpreciseFraction? = null, - complexity: Double? = null, - priority: Int? = null, - errorOnFailure: Boolean = false, - matterFunctions: List> = ImmutableList.of(), - complexityFunctions: List> = ImmutableList.of(), - priorityFunctions: List> = ImmutableList.of(), - ) : super(tag = tag, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { - check(matter != null || complexity != null || priority != null || matterFunctions.isNotEmpty() || complexityFunctions.isNotEmpty() || priorityFunctions.isNotEmpty()) { - "Can't update existing entries without anything specified" - } - - this.matterFunctions = ImmutableList.copyOf(matterFunctions) - this.complexityFunctions = ImmutableList.copyOf(complexityFunctions) - this.priorityFunctions = ImmutableList.copyOf(priorityFunctions) - } - - constructor( - key: ResourceLocation, - matter: ImpreciseFraction? = null, - complexity: Double? = null, - priority: Int? = null, - errorOnFailure: Boolean = false, - matterFunctions: List> = ImmutableList.of(), - complexityFunctions: List> = ImmutableList.of(), - priorityFunctions: List> = ImmutableList.of(), - ) : super(key = key, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { - check(matter != null || complexity != null || priority != null || matterFunctions.isNotEmpty() || complexityFunctions.isNotEmpty() || priorityFunctions.isNotEmpty()) { - "Can't update existing entries without anything specified" - } - - this.matterFunctions = ImmutableList.copyOf(matterFunctions) - this.complexityFunctions = ImmutableList.copyOf(complexityFunctions) - this.priorityFunctions = ImmutableList.copyOf(priorityFunctions) - } - - override fun toJson(): JsonObject { - return super.toJson().also { - it["action"] = "update" - - if (matterFunctions.isNotEmpty()) { - it["matter_functions"] = JsonArray().also { - for (fn in matterFunctions) { - it.add(fn.toJson()) - } - } - } - - if (complexityFunctions.isNotEmpty()) { - it["complexity_functions"] = JsonArray().also { - for (fn in complexityFunctions) { - it.add(fn.toJson()) - } - } - } - - if (priorityFunctions.isNotEmpty()) { - it["priority_functions"] = JsonArray().also { - for (fn in priorityFunctions) { - it.add(fn.toJson()) - } - } - } - } - } - - fun apply(value: MutableEntry, modifier: ResourceLocation) { - if (matterFunctions.isNotEmpty()) - value.matter = apply(value.matter, matterFunctions) - else if (matter != null) - value.matter = matter - - if (complexityFunctions.isNotEmpty()) - value.complexity = apply(value.complexity, complexityFunctions) - else if (complexity != null) - value.complexity = complexity - - if (priorityFunctions.isNotEmpty()) - value.priority = apply(value.priority, priorityFunctions) - else if (priority != null) - value.priority = priority - - value.modificationChain.add(modifier) - } - - override fun update(registry: MutableCollection, modifier: ResourceLocation) { - if(!( - matter != null || - complexity != null || - priority != null || - matterFunctions.isNotEmpty() || - complexityFunctions.isNotEmpty() || - priorityFunctions.isNotEmpty() - )) { - throw JsonParseException("Can't update existing entries without anything specified (for $modifier)") - } - - if (matter != null && matterFunctions.isNotEmpty()) { - throw JsonParseException("Can't have both matter and matter functions specified at the same time (for $modifier)") - } - - if (complexity != null && complexityFunctions.isNotEmpty()) { - throw JsonParseException("Can't have both complexity and complexity functions specified at the same time (for $modifier)") - } - - if (priority != null && priorityFunctions.isNotEmpty()) { - throw JsonParseException("Can't have both priority and priority functions specified at the same time (for $modifier)") - } - - if (!checkConditions()) { - return - } - - if (key != null) { - val iterator = registry.iterator() - - for (value in iterator) { - if (value is MutableKeyEntry && value.key == key) { - apply(value, modifier) - return - } - } - - fail { "Could not find matter value with key $key" } - } else { - val iterator = registry.iterator() - - for (value in iterator) { - if (value is MutableTagEntry && value.tag == tag) { - apply(value, modifier) - return - } - } - - fail { "Could not find matter value with tag $tag" } - } - } - } - sealed class MutableEntry( val modificationChain: MutableList, var matter: ImpreciseFraction, @@ -679,46 +76,6 @@ object MatterManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyP } } - interface IMatterValue : Comparable { - val matter: ImpreciseFraction - val complexity: Double - - val hasMatterValue: Boolean get() = matter.isPositive && complexity > 0.0 - - operator fun plus(other: IMatterValue): IMatterValue { - return MatterValue(matter + other.matter, complexity + other.complexity) - } - - operator fun minus(other: IMatterValue): IMatterValue { - return MatterValue(matter - other.matter, complexity - other.complexity) - } - - override fun compareTo(other: IMatterValue): Int { - val matterComparison = matter.compareTo(other.matter) - - if (matterComparison == 0) { - return complexity.compareTo(other.complexity) - } - - return matterComparison - } - - /** - * ZERO - */ - companion object : IMatterValue { - override val matter: ImpreciseFraction - get() = ImpreciseFraction.ZERO - override val complexity: Double - get() = 0.0 - } - } - - data class MatterValue( - override val matter: ImpreciseFraction, - override val complexity: Double - ) : IMatterValue - sealed class Entry( val modificationChain: List, final override val matter: ImpreciseFraction, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt index c3a1c05c4..d6ff4d2f9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt @@ -220,14 +220,14 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se } } - fun getDetermined(index: Item): MatterManager.IMatterValue { - return determinedValues[index] ?: MatterManager.IMatterValue.Companion + fun getDetermined(index: Item): IMatterValue { + return determinedValues[index] ?: IMatterValue.Companion } private var input2Recipes: Map> = mapOf() private var output2Recipes: Map> = mapOf() - private val determinedValues = Reference2ObjectOpenHashMap() + private val determinedValues = Reference2ObjectOpenHashMap() private val commentary = Reference2ObjectOpenHashMap() private val seenItems = ArrayDeque() @@ -235,8 +235,8 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se return commentary[item]?.copy() } - private data class Result(val type: Type, val value: MatterManager.IMatterValue? = null) { - constructor(value: MatterManager.IMatterValue) : this(Type.RESOLVED, value) + private data class Result(val type: Type, val value: IMatterValue? = null) { + constructor(value: IMatterValue) : this(Type.RESOLVED, value) val isSkipped get() = type === Type.SKIPPED val isMissing get() = type === Type.MISSING @@ -280,7 +280,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se var accumulatedComplexity: Double? = null inputsLoop@ for ((i, inputs) in recipe.inputs.withIndex()) { - var minimal: MatterManager.IMatterValue? = null + var minimal: IMatterValue? = null var minimalMultiplier = 0.0 for (input in inputs) { @@ -341,7 +341,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se return Result.MISSING } - val result = MatterManager.MatterValue(minimalMatter, minimalComplexity) + val result = MatterValue(minimalMatter, minimalComplexity) changes = true determinedValues[item] = result @@ -357,7 +357,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se return getResult } - var value: MatterManager.IMatterValue? = MatterManager.getDirect(item) + var value: IMatterValue? = MatterManager.getDirect(item) if (value?.hasMatterValue == true) { return Result(value) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/UpdateAction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/UpdateAction.kt new file mode 100644 index 000000000..c46bd76bb --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/UpdateAction.kt @@ -0,0 +1,309 @@ +package ru.dbotthepony.mc.otm.matter + +import com.google.common.collect.ImmutableList +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParseException +import net.minecraft.resources.ResourceLocation +import net.minecraft.tags.TagKey +import net.minecraft.world.item.Item +import ru.dbotthepony.mc.otm.core.ImpreciseFraction +import ru.dbotthepony.mc.otm.core.integerDivisionDown +import ru.dbotthepony.mc.otm.core.integerDivisionUp +import ru.dbotthepony.mc.otm.core.set +import ru.dbotthepony.mc.otm.data.stream + +class UpdateAction : AbstractRegistryAction { + enum class Function { + ADD { + override fun updateValue(self: Int, other: Int): Int = self + other + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self + other + override fun updateValue(self: Double, other: Double): Double = self + other + }, + SUBTRACT { + override fun updateValue(self: Int, other: Int): Int = self - other + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self - other + override fun updateValue(self: Double, other: Double): Double = self - other + }, + MULTIPLY { + override fun updateValue(self: Int, other: Int): Int = self * other + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self * other + override fun updateValue(self: Double, other: Double): Double = self * other + }, + DIVIDE { + override fun updateValue(self: Int, other: Int): Int = self / other + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self / other + override fun updateValue(self: Double, other: Double): Double = self / other + }, + DIVIDE_UP { + override fun updateValue(self: Int, other: Int): Int = integerDivisionUp(self, other) + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = throw JsonParseException( + "Integer division up is available only for integers" + ) + override fun updateValue(self: Double, other: Double): Double = throw JsonParseException( + "Integer division up is available only for integers" + ) + }, + DIVIDE_DOWN { + override fun updateValue(self: Int, other: Int): Int = integerDivisionDown(self, other) + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = throw JsonParseException( + "Integer division down is available only for integers" + ) + override fun updateValue(self: Double, other: Double): Double = throw JsonParseException( + "Integer division down is available only for integers" + ) + }, + MODULO { + override fun updateValue(self: Int, other: Int): Int = self % other + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self % other + override fun updateValue(self: Double, other: Double): Double = self % other + }, + AT_LEAST { + override fun updateValue(self: Int, other: Int): Int = self.coerceAtLeast(other) + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self.coerceAtLeast(other) + override fun updateValue(self: Double, other: Double): Double = self.coerceAtLeast(other) + }, + AT_MOST { + override fun updateValue(self: Int, other: Int): Int = self.coerceAtMost(other) + override fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction = self.coerceAtMost(other) + override fun updateValue(self: Double, other: Double): Double = self.coerceAtMost(other) + }, + ; + + protected abstract fun updateValue(self: Int, other: Int): Int + protected abstract fun updateValue(self: ImpreciseFraction, other: ImpreciseFraction): ImpreciseFraction + protected abstract fun updateValue(self: Double, other: Double): Double + + fun updateValue(self: T, other: T): T { + return when (self) { + is ImpreciseFraction -> updateValue(self, other) + is Double -> updateValue(self, other) + is Int -> updateValue(self, other) + else -> throw RuntimeException("Unknown number type: ${self::class.qualifiedName}") + } + } + } + + class BoundFunction(val function: Function, val value: T) { + fun apply(self: T): T { + return function.updateValue(self, value) + } + + fun toJson(): JsonObject { + return JsonObject().also { + it["type"] = function.name + + if (value is Int || value is Double) + it["value"] = value + else + it["value"] = value.toString() + } + } + } + + companion object { + inline fun getValue(value: JsonElement): T { + return when (T::class) { + ImpreciseFraction::class -> ImpreciseFraction(value.asString) as T + Double::class -> value.asDouble as T + Int::class -> value.asInt as T + else -> throw RuntimeException("Unknown number type: ${T::class.qualifiedName}") + } + } + + inline fun deserializeFunctionTree(input: JsonElement?, name: String): List> { + if (input == null) { + return listOf() + } + + if (input !is JsonArray) { + throw JsonParseException("Expected $name to be JsonArray, ${input::class.qualifiedName} given") + } + + return input.stream().map { + if (it !is JsonObject) { + throw JsonParseException("All elements in function tree must be JsonObjects, ${it::class.qualifiedName} given") + } + + val type = it["type"]?.asString ?: throw JsonParseException("Invalid `type` value in function object") + val value = it["value"] ?: throw JsonParseException("Missing `value` value in function object") + + val parsedValue: T = getValue(value) + val fn: Function + + try { + fn = Function.valueOf(type.uppercase()) + } catch (err: NoSuchElementException) { + throw JsonParseException("No such function: $type", err) + } + + return@map BoundFunction(fn, parsedValue) + }.collect(ImmutableList.toImmutableList()) + } + + fun apply(value: T, funcs: Collection>): T { + if (funcs.isEmpty()) { + return value + } + + var newValue = value + + for (func in funcs) { + newValue = func.apply(newValue) + } + + return newValue + } + } + + val matterFunctions: List> + val complexityFunctions: List> + val priorityFunctions: List> + + constructor(json: JsonObject) : super(json) { + matterFunctions = deserializeFunctionTree(json["matter_functions"], "matter_functions") + complexityFunctions = deserializeFunctionTree(json["complexity_functions"], "complexity_functions") + priorityFunctions = deserializeFunctionTree(json["priority_functions"], "priority_functions") + } + + constructor( + tag: TagKey, + matter: ImpreciseFraction? = null, + complexity: Double? = null, + priority: Int? = null, + errorOnFailure: Boolean = false, + matterFunctions: List> = ImmutableList.of(), + complexityFunctions: List> = ImmutableList.of(), + priorityFunctions: List> = ImmutableList.of(), + ) : super(tag = tag, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { + check(matter != null || complexity != null || priority != null || matterFunctions.isNotEmpty() || complexityFunctions.isNotEmpty() || priorityFunctions.isNotEmpty()) { + "Can't update existing entries without anything specified" + } + + this.matterFunctions = ImmutableList.copyOf(matterFunctions) + this.complexityFunctions = ImmutableList.copyOf(complexityFunctions) + this.priorityFunctions = ImmutableList.copyOf(priorityFunctions) + } + + constructor( + key: ResourceLocation, + matter: ImpreciseFraction? = null, + complexity: Double? = null, + priority: Int? = null, + errorOnFailure: Boolean = false, + matterFunctions: List> = ImmutableList.of(), + complexityFunctions: List> = ImmutableList.of(), + priorityFunctions: List> = ImmutableList.of(), + ) : super(key = key, matter = matter, complexity = complexity, priority = priority, errorOnFailure = errorOnFailure) { + check(matter != null || complexity != null || priority != null || matterFunctions.isNotEmpty() || complexityFunctions.isNotEmpty() || priorityFunctions.isNotEmpty()) { + "Can't update existing entries without anything specified" + } + + this.matterFunctions = ImmutableList.copyOf(matterFunctions) + this.complexityFunctions = ImmutableList.copyOf(complexityFunctions) + this.priorityFunctions = ImmutableList.copyOf(priorityFunctions) + } + + override fun toJson(): JsonObject { + return super.toJson().also { + it["action"] = "update" + + if (matterFunctions.isNotEmpty()) { + it["matter_functions"] = JsonArray().also { + for (fn in matterFunctions) { + it.add(fn.toJson()) + } + } + } + + if (complexityFunctions.isNotEmpty()) { + it["complexity_functions"] = JsonArray().also { + for (fn in complexityFunctions) { + it.add(fn.toJson()) + } + } + } + + if (priorityFunctions.isNotEmpty()) { + it["priority_functions"] = JsonArray().also { + for (fn in priorityFunctions) { + it.add(fn.toJson()) + } + } + } + } + } + + fun apply(value: MatterManager.MutableEntry, modifier: ResourceLocation) { + if (matterFunctions.isNotEmpty()) + value.matter = apply(value.matter, matterFunctions) + else if (matter != null) + value.matter = matter + + if (complexityFunctions.isNotEmpty()) + value.complexity = apply(value.complexity, complexityFunctions) + else if (complexity != null) + value.complexity = complexity + + if (priorityFunctions.isNotEmpty()) + value.priority = apply(value.priority, priorityFunctions) + else if (priority != null) + value.priority = priority + + value.modificationChain.add(modifier) + } + + override fun update(registry: MutableCollection, modifier: ResourceLocation) { + if(!( + matter != null || + complexity != null || + priority != null || + matterFunctions.isNotEmpty() || + complexityFunctions.isNotEmpty() || + priorityFunctions.isNotEmpty() + )) { + throw JsonParseException("Can't update existing entries without anything specified (for $modifier)") + } + + if (matter != null && matterFunctions.isNotEmpty()) { + throw JsonParseException("Can't have both matter and matter functions specified at the same time (for $modifier)") + } + + if (complexity != null && complexityFunctions.isNotEmpty()) { + throw JsonParseException("Can't have both complexity and complexity functions specified at the same time (for $modifier)") + } + + if (priority != null && priorityFunctions.isNotEmpty()) { + throw JsonParseException("Can't have both priority and priority functions specified at the same time (for $modifier)") + } + + if (!checkConditions()) { + return + } + + if (key != null) { + val iterator = registry.iterator() + + for (value in iterator) { + if (value is MatterManager.MutableKeyEntry && value.key == key) { + apply(value, modifier) + return + } + } + + fail { "Could not find matter value with key $key" } + } else { + val iterator = registry.iterator() + + for (value in iterator) { + if (value is MatterManager.MutableTagEntry && value.tag == tag) { + apply(value, modifier) + return + } + } + + fail { "Could not find matter value with tag $tag" } + } + } +}