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.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.matter.IMatterItem import ru.dbotthepony.mc.otm.matter.IMatterItem
import ru.dbotthepony.mc.otm.core.set 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 { class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREATIVE_TAB).stacksTo(64)), IMatterItem {
private fun matter(stack: ItemStack): ImpreciseFraction { private fun matter(stack: ItemStack): ImpreciseFraction {
@ -22,9 +23,9 @@ class MatterDustItem : Item(Properties().tab(OverdriveThatMatters.INSTANCE.CREAT
stack.orCreateTag["matter"] = matter.serializeNBT() 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 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 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 import net.minecraft.world.item.ItemStack
interface IMatterItem { interface IMatterItem {
fun getMatterValue(stack: ItemStack): MatterManager.IMatterValue? fun getMatterValue(stack: ItemStack): IMatterValue?
fun hasMatterValue(stack: ItemStack) = getMatterValue(stack) != null fun hasMatterValue(stack: ItemStack) = getMatterValue(stack) != null
fun canDecompose(stack: ItemStack): Boolean 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) constructor(event: GatherDataEvent) : this(event.generator, event.modContainer.namespace)
protected val pathProvider: DataGenerator.PathProvider = dataGenerator.createPathProvider(DataGenerator.Target.DATA_PACK, MatterManager.DIRECTORY) 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) { sealed class Configuration(val name: ResourceLocation) {
var errorOnFailure: Boolean = false 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()}" } 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) { class InsertConfiguration(name: ResourceLocation) : Configuration(name) {
var replaceIfExists: Boolean = false var replaceIfExists: Boolean = false
var comparePriority: Boolean = false var comparePriority: Boolean = false
override fun build(): MatterManager.InsertAction { override fun build(): InsertAction {
checkKeyTag() checkKeyTag()
checkMatterValues() checkMatterValues()
check(matter != null && complexity != null) { "You must define both matter value and complexity for $name" } check(matter != null && complexity != null) { "You must define both matter value and complexity for $name" }
if (key != null) { if (key != null) {
return MatterManager.InsertAction( return InsertAction(
key = key ?: throw ConcurrentModificationException(), key = key ?: throw ConcurrentModificationException(),
matter = matter, matter = matter,
complexity = complexity, complexity = complexity,
@ -77,7 +77,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
comparePriority = comparePriority comparePriority = comparePriority
) )
} else { } else {
return MatterManager.InsertAction( return InsertAction(
tag = tag ?: throw ConcurrentModificationException(), tag = tag ?: throw ConcurrentModificationException(),
matter = matter, matter = matter,
complexity = complexity, complexity = complexity,
@ -91,19 +91,19 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
} }
class DeleteConfiguration(name: ResourceLocation) : Configuration(name) { class DeleteConfiguration(name: ResourceLocation) : Configuration(name) {
override fun build(): MatterManager.DeleteAction { override fun build(): DeleteAction {
checkKeyTag() checkKeyTag()
check(matter == null) { "Delete action can't have matter value" } check(matter == null) { "Delete action can't have matter value" }
check(complexity == null) { "Delete action can't have complexity value" } check(complexity == null) { "Delete action can't have complexity value" }
if (key != null) { if (key != null) {
return MatterManager.DeleteAction( return DeleteAction(
key = key ?: throw ConcurrentModificationException(), key = key ?: throw ConcurrentModificationException(),
errorOnFailure = errorOnFailure, errorOnFailure = errorOnFailure,
) )
} else { } else {
return MatterManager.DeleteAction( return DeleteAction(
tag = tag ?: throw ConcurrentModificationException(), tag = tag ?: throw ConcurrentModificationException(),
errorOnFailure = errorOnFailure, errorOnFailure = errorOnFailure,
) )
@ -112,29 +112,29 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
} }
class UpdateConfiguration(name: ResourceLocation) : Configuration(name) { class UpdateConfiguration(name: ResourceLocation) : Configuration(name) {
val matterFunctions = ArrayList<MatterManager.UpdateAction.BoundFunction<ImpreciseFraction>>() val matterFunctions = ArrayList<UpdateAction.BoundFunction<ImpreciseFraction>>()
val complexityFunctions = ArrayList<MatterManager.UpdateAction.BoundFunction<Double>>() val complexityFunctions = ArrayList<UpdateAction.BoundFunction<Double>>()
val priorityFunctions = ArrayList<MatterManager.UpdateAction.BoundFunction<Int>>() val priorityFunctions = ArrayList<UpdateAction.BoundFunction<Int>>()
fun addMatterFunction(function: MatterManager.UpdateAction.Function, value: ImpreciseFraction): UpdateConfiguration { fun addMatterFunction(function: UpdateAction.Function, value: ImpreciseFraction): UpdateConfiguration {
matterFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) matterFunctions.add(UpdateAction.BoundFunction(function, value))
matter = null matter = null
return this return this
} }
fun addComplexityFunction(function: MatterManager.UpdateAction.Function, value: Double): UpdateConfiguration { fun addComplexityFunction(function: UpdateAction.Function, value: Double): UpdateConfiguration {
complexityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) complexityFunctions.add(UpdateAction.BoundFunction(function, value))
matter = null matter = null
return this return this
} }
fun addPriorityFunction(function: MatterManager.UpdateAction.Function, value: Int): UpdateConfiguration { fun addPriorityFunction(function: UpdateAction.Function, value: Int): UpdateConfiguration {
priorityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value)) priorityFunctions.add(UpdateAction.BoundFunction(function, value))
matter = null matter = null
return this 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(!(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(!(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" } 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() checkKeyTag()
if (key != null) { if (key != null) {
return MatterManager.UpdateAction( return UpdateAction(
key = key ?: throw ConcurrentModificationException(), key = key ?: throw ConcurrentModificationException(),
matter = matter, matter = matter,
complexity = complexity, complexity = complexity,
@ -160,7 +160,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
priorityFunctions = priorityFunctions priorityFunctions = priorityFunctions
) )
} else { } else {
return MatterManager.UpdateAction( return UpdateAction(
tag = tag ?: throw ConcurrentModificationException(), tag = tag ?: throw ConcurrentModificationException(),
matter = matter, matter = matter,
complexity = complexity, 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" } check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = InsertConfiguration(name) val config = InsertConfiguration(name)
configurator.invoke(config) configurator.invoke(config)
@ -744,7 +744,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
return built 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" } check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = DeleteConfiguration(name) val config = DeleteConfiguration(name)
configurator.invoke(config) configurator.invoke(config)
@ -753,7 +753,7 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
return built 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" } check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = UpdateConfiguration(name) val config = UpdateConfiguration(name)
configurator.invoke(config) 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/")) { return insert(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) {
key = name.asItem().registryName ?: throw ConcurrentModificationException() key = name.asItem().registryName ?: throw ConcurrentModificationException()
configurator.invoke(this) 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/")) { return delete(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) {
key = name.asItem().registryName ?: throw ConcurrentModificationException() key = name.asItem().registryName ?: throw ConcurrentModificationException()
configurator.invoke(this) 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/")) { return update(updateLocation(name.asItem().registryName ?: throw NullPointerException("${name.asItem()} does not have registry name!"), "item/")) {
key = name.asItem().registryName ?: throw ConcurrentModificationException() key = name.asItem().registryName ?: throw ConcurrentModificationException()
configurator.invoke(this) 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/")) { return insert(updateLocation(name.location, "tag/")) {
tag = name tag = name
configurator.invoke(this) 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/")) { return delete(updateLocation(name.location, "tag/")) {
tag = name tag = name
configurator.invoke(this) 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/")) { return update(updateLocation(name.location, "tag/")) {
tag = name tag = name
configurator.invoke(this) configurator.invoke(this)
@ -886,8 +886,8 @@ open class MatterDataProvider(protected val dataGenerator: DataGenerator, val na
*/ */
protected open fun addActions() {} protected open fun addActions() {}
protected val added = ArrayList<MatterManager.AbstractRegistryAction>() protected val added = ArrayList<AbstractRegistryAction>()
val addedView: List<MatterManager.AbstractRegistryAction> = Collections.unmodifiableList(added) val addedView: List<AbstractRegistryAction> = Collections.unmodifiableList(added)
final override fun run(output: CachedOutput) { final override fun run(output: CachedOutput) {
addActions() addActions()

View File

@ -2,21 +2,16 @@ package ru.dbotthepony.mc.otm.matter
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener
import net.minecraft.tags.ItemTags
import net.minecraft.tags.TagKey import net.minecraft.tags.TagKey
import net.minecraft.util.profiling.ProfilerFiller import net.minecraft.util.profiling.ProfilerFiller
import net.minecraft.world.item.Item 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.TranslatableComponent
import ru.dbotthepony.mc.otm.core.formatMatterFull import ru.dbotthepony.mc.otm.core.formatMatterFull
import ru.dbotthepony.mc.otm.core.formatSiComponent 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.isZero
import ru.dbotthepony.mc.otm.core.orNull 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.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.stream
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import java.math.BigInteger import java.math.BigInteger
import java.util.Collections import java.util.Collections
@ -53,599 +43,6 @@ import kotlin.math.pow
object MatterManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_matter") { object MatterManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_matter") {
const val DIRECTORY = "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( sealed class MutableEntry(
val modificationChain: MutableList<ResourceLocation>, val modificationChain: MutableList<ResourceLocation>,
var matter: ImpreciseFraction, 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( sealed class Entry(
val modificationChain: List<ResourceLocation>, val modificationChain: List<ResourceLocation>,
final override val matter: ImpreciseFraction, final override val matter: ImpreciseFraction,

View File

@ -220,14 +220,14 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se
} }
} }
fun getDetermined(index: Item): MatterManager.IMatterValue { fun getDetermined(index: Item): IMatterValue {
return determinedValues[index] ?: MatterManager.IMatterValue.Companion return determinedValues[index] ?: IMatterValue.Companion
} }
private var input2Recipes: Map<Item, Collection<ResolvedRecipe>> = mapOf() private var input2Recipes: Map<Item, Collection<ResolvedRecipe>> = mapOf()
private var output2Recipes: 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 commentary = Reference2ObjectOpenHashMap<Item, Component>()
private val seenItems = ArrayDeque<Item>() private val seenItems = ArrayDeque<Item>()
@ -235,8 +235,8 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se
return commentary[item]?.copy() return commentary[item]?.copy()
} }
private data class Result(val type: Type, val value: MatterManager.IMatterValue? = null) { private data class Result(val type: Type, val value: IMatterValue? = null) {
constructor(value: MatterManager.IMatterValue) : this(Type.RESOLVED, value) constructor(value: IMatterValue) : this(Type.RESOLVED, value)
val isSkipped get() = type === Type.SKIPPED val isSkipped get() = type === Type.SKIPPED
val isMissing get() = type === Type.MISSING val isMissing get() = type === Type.MISSING
@ -280,7 +280,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se
var accumulatedComplexity: Double? = null var accumulatedComplexity: Double? = null
inputsLoop@ for ((i, inputs) in recipe.inputs.withIndex()) { inputsLoop@ for ((i, inputs) in recipe.inputs.withIndex()) {
var minimal: MatterManager.IMatterValue? = null var minimal: IMatterValue? = null
var minimalMultiplier = 0.0 var minimalMultiplier = 0.0
for (input in inputs) { for (input in inputs) {
@ -341,7 +341,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se
return Result.MISSING return Result.MISSING
} }
val result = MatterManager.MatterValue(minimalMatter, minimalComplexity) val result = MatterValue(minimalMatter, minimalComplexity)
changes = true changes = true
determinedValues[item] = result determinedValues[item] = result
@ -357,7 +357,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se
return getResult return getResult
} }
var value: MatterManager.IMatterValue? = MatterManager.getDirect(item) var value: IMatterValue? = MatterManager.getDirect(item)
if (value?.hasMatterValue == true) { if (value?.hasMatterValue == true) {
return Result(value) 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" }
}
}
}