Slice and splice classes

This commit is contained in:
DBotThePony 2022-11-06 16:29:38 +07:00
parent b1bd0aa2b5
commit 11f876ab57
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 765 additions and 686 deletions

View File

@ -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

View File

@ -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>) : Condition() {
override fun test(): Boolean {
return conditions.all { it.test() }
}
}
val errorOnFailure: Boolean
val tag: TagKey<Item>?
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<Item>,
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<MatterManager.MutableEntry>, 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
}
}
}

View File

@ -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<Item>,
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<MatterManager.MutableEntry>, 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" }
}
}
}

View File

@ -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
}

View File

@ -0,0 +1,43 @@
package ru.dbotthepony.mc.otm.matter
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
interface IMatterValue : Comparable<IMatterValue> {
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

View File

@ -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<Item>,
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<MatterManager.MutableEntry>, 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
)
)
}
}
}
}

View File

@ -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<ResourceLocation, MatterManager.AbstractRegistryAction>()
protected val actions = LinkedHashMap<ResourceLocation, AbstractRegistryAction>()
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<MatterManager.UpdateAction.BoundFunction<ImpreciseFraction>>()
val complexityFunctions = ArrayList<MatterManager.UpdateAction.BoundFunction<Double>>()
val priorityFunctions = ArrayList<MatterManager.UpdateAction.BoundFunction<Int>>()
val matterFunctions = ArrayList<UpdateAction.BoundFunction<ImpreciseFraction>>()
val complexityFunctions = ArrayList<UpdateAction.BoundFunction<Double>>()
val priorityFunctions = ArrayList<UpdateAction.BoundFunction<Int>>()
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<Item>, configurator: InsertConfiguration.() -> Unit): MatterManager.InsertAction {
fun insert(name: TagKey<Item>, configurator: InsertConfiguration.() -> Unit): InsertAction {
return insert(updateLocation(name.location, "tag/")) {
tag = name
configurator.invoke(this)
}
}
fun delete(name: TagKey<Item>, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction {
fun delete(name: TagKey<Item>, configurator: DeleteConfiguration.() -> Unit): DeleteAction {
return delete(updateLocation(name.location, "tag/")) {
tag = name
configurator.invoke(this)
}
}
fun update(name: TagKey<Item>, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction {
fun update(name: TagKey<Item>, 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<MatterManager.AbstractRegistryAction>()
val addedView: List<MatterManager.AbstractRegistryAction> = Collections.unmodifiableList(added)
protected val added = ArrayList<AbstractRegistryAction>()
val addedView: List<AbstractRegistryAction> = Collections.unmodifiableList(added)
final override fun run(output: CachedOutput) {
addActions()

View File

@ -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>) : Condition() {
override fun test(): Boolean {
return conditions.all { it.test() }
}
}
val errorOnFailure: Boolean
val tag: TagKey<Item>?
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<Item>,
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<MutableEntry>, 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<Item>,
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<MutableEntry>, 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<Item>,
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<MutableEntry>, 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 <T : Number> 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<T : Number>(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 <reified T : Number> 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 <reified T : Number> deserializeFunctionTree(input: JsonElement?, name: String): List<BoundFunction<T>> {
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 <T : Number> apply(value: T, funcs: Collection<BoundFunction<T>>): T {
if (funcs.isEmpty()) {
return value
}
var newValue = value
for (func in funcs) {
newValue = func.apply(newValue)
}
return newValue
}
}
val matterFunctions: List<BoundFunction<ImpreciseFraction>>
val complexityFunctions: List<BoundFunction<Double>>
val priorityFunctions: List<BoundFunction<Int>>
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<Item>,
matter: ImpreciseFraction? = null,
complexity: Double? = null,
priority: Int? = null,
errorOnFailure: Boolean = false,
matterFunctions: List<BoundFunction<ImpreciseFraction>> = ImmutableList.of(),
complexityFunctions: List<BoundFunction<Double>> = ImmutableList.of(),
priorityFunctions: List<BoundFunction<Int>> = 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<BoundFunction<ImpreciseFraction>> = ImmutableList.of(),
complexityFunctions: List<BoundFunction<Double>> = ImmutableList.of(),
priorityFunctions: List<BoundFunction<Int>> = 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<MutableEntry>, 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<ResourceLocation>,
var matter: ImpreciseFraction,
@ -679,46 +76,6 @@ object MatterManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyP
}
}
interface IMatterValue : Comparable<IMatterValue> {
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<ResourceLocation>,
final override val matter: ImpreciseFraction,

View File

@ -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<Item, Collection<ResolvedRecipe>> = mapOf()
private var output2Recipes: Map<Item, Collection<ResolvedRecipe>> = mapOf()
private val determinedValues = Reference2ObjectOpenHashMap<Item, MatterManager.IMatterValue>()
private val determinedValues = Reference2ObjectOpenHashMap<Item, IMatterValue>()
private val commentary = Reference2ObjectOpenHashMap<Item, Component>()
private val seenItems = ArrayDeque<Item>()
@ -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)

View File

@ -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 <T : Number> 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<T : Number>(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 <reified T : Number> 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 <reified T : Number> deserializeFunctionTree(input: JsonElement?, name: String): List<BoundFunction<T>> {
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 <T : Number> apply(value: T, funcs: Collection<BoundFunction<T>>): T {
if (funcs.isEmpty()) {
return value
}
var newValue = value
for (func in funcs) {
newValue = func.apply(newValue)
}
return newValue
}
}
val matterFunctions: List<BoundFunction<ImpreciseFraction>>
val complexityFunctions: List<BoundFunction<Double>>
val priorityFunctions: List<BoundFunction<Int>>
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<Item>,
matter: ImpreciseFraction? = null,
complexity: Double? = null,
priority: Int? = null,
errorOnFailure: Boolean = false,
matterFunctions: List<BoundFunction<ImpreciseFraction>> = ImmutableList.of(),
complexityFunctions: List<BoundFunction<Double>> = ImmutableList.of(),
priorityFunctions: List<BoundFunction<Int>> = 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<BoundFunction<ImpreciseFraction>> = ImmutableList.of(),
complexityFunctions: List<BoundFunction<Double>> = ImmutableList.of(),
priorityFunctions: List<BoundFunction<Int>> = 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<MatterManager.MutableEntry>, 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" }
}
}
}