New matter values system checkpoint

This commit is contained in:
DBotThePony 2022-10-29 00:39:45 +07:00
parent a178c88bf2
commit a757fcc9ab
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 1150 additions and 2 deletions

View File

@ -47,6 +47,7 @@ import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes
import ru.dbotthepony.mc.otm.datagen.tags.TagsProvider
import ru.dbotthepony.mc.otm.datagen.tags.addTags
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
import kotlin.properties.Delegates
@ -73,6 +74,8 @@ object DataGen {
private set
var researchProvider: AndroidResearchDataProvider by WriteOnce()
private set
var matterData: MatterDataProvider by WriteOnce()
private set
fun decorativeCubeAll(vararg blocks: Block) {
blockModelProvider.decorativeCubeAll(*blocks)
@ -401,6 +404,7 @@ object DataGen {
val recipeProvider = MatteryRecipeProvider(event.generator)
val lootModifier = LootModifiers(event.generator)
val languageProvider = MatteryLanguageProvider(event.generator)
val matterData = MatterDataProvider(event)
val researchProvider = AndroidResearchDataProvider(event.generator).also { it.exec { addResearchData(it, languageProvider) } }
this.blockModelProvider = blockModelProvider
@ -411,6 +415,7 @@ object DataGen {
this.lootModifier = lootModifier
this.languageProvider = languageProvider
this.researchProvider = researchProvider
this.matterData = matterData
val tagsProvider = TagsProvider(event)
val advancementProvider = AdvancementProvider(event)
@ -428,6 +433,7 @@ object DataGen {
event.generator.addProvider(true, SoundDataProvider(event))
event.generator.addProvider(true, researchProvider)
event.generator.addProvider(true, advancementProvider)
event.generator.addProvider(true, matterData)
AddEnglishLanguage(languageProvider)

View File

@ -39,6 +39,7 @@ import ru.dbotthepony.mc.otm.item.QuantumBatteryItem;
import ru.dbotthepony.mc.otm.item.weapon.AbstractWeaponItem;
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem;
import ru.dbotthepony.mc.otm.matter.MatterDataKt;
import ru.dbotthepony.mc.otm.matter.MatterManager;
import ru.dbotthepony.mc.otm.matter.MatterRegistryKt;
import ru.dbotthepony.mc.otm.network.*;
import ru.dbotthepony.mc.otm.registry.*;
@ -142,6 +143,8 @@ public final class OverdriveThatMatters {
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::reloadEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::syncEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::reloadEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStopping);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onLevelUnload);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch);

View File

@ -6,6 +6,7 @@ package ru.dbotthepony.mc.otm.core
import com.google.common.collect.ImmutableList
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import net.minecraft.core.BlockPos
import net.minecraft.nbt.ByteArrayTag
import net.minecraft.nbt.CompoundTag
@ -239,6 +240,9 @@ fun FriendlyByteBuf.readBigInteger(byteLimit: Int = 128) = BigInteger(readByteAr
operator fun IItemHandler.get(index: Int): ItemStack = getStackInSlot(index)
operator fun JsonObject.set(s: String, value: JsonElement) = add(s, value)
operator fun JsonObject.set(s: String, value: String) = add(s, JsonPrimitive(value))
operator fun JsonObject.set(s: String, value: Number) = add(s, JsonPrimitive(value))
operator fun JsonObject.set(s: String, value: Boolean) = add(s, JsonPrimitive(value))
fun <T> LazyOptional<T>.orNull(): T? {
if (!isPresent) {

View File

@ -753,6 +753,10 @@ class ImpreciseFraction @JvmOverloads constructor(whole: BigInteger, decimal: Do
return toBigDecmial().divide(divisor.toBigDecmial(), PERCENTAGE_CONTEXT).toFloat()
}
operator fun rem(divisor: ImpreciseFraction): ImpreciseFraction {
TODO("Not yet implemented")
}
companion object {
@JvmStatic
val serialVersionUID: Long = 287354739494574838L

View File

@ -0,0 +1,288 @@
package ru.dbotthepony.mc.otm.matter
import net.minecraft.data.CachedOutput
import net.minecraft.data.DataGenerator
import net.minecraft.data.DataProvider
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import net.minecraft.world.level.ItemLike
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.core.registryName
import java.util.Collections
import java.util.function.Consumer
open class MatterDataProvider(protected val dataGenerator: DataGenerator, val namespace: String?) : DataProvider {
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>()
sealed class Configuration(val name: ResourceLocation) {
var errorOnFailure: Boolean = false
var matter: ImpreciseFraction? = null
var complexity: Double? = null
var priority: Int? = null
private var _key: ResourceLocation? = null
private var _tag: TagKey<Item>? = null
var key: ResourceLocation?
get() = _key
set(value) {
checkNotNull(value) { "Can't set key to null" }
_tag = null
_key = value
}
var tag: TagKey<Item>?
get() = _tag
set(value) {
checkNotNull(value) { "Can't set tag to null" }
_key = null
_tag = value
}
protected fun checkKeyTag() {
check(_key != null || _tag != null) { "You must define either key or tag for $name" }
}
protected fun checkMatterValues() {
check(matter == null || (matter ?: throw ConcurrentModificationException()) >= ImpreciseFraction.ZERO) { "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
}
class InsertConfiguration(name: ResourceLocation) : Configuration(name) {
var replaceIfExists: Boolean = false
var comparePriority: Boolean = false
override fun build(): MatterManager.InsertAction {
checkKeyTag()
checkMatterValues()
check(matter != null && complexity != null) { "You must define both matter value and complexity for $name" }
if (key != null) {
return MatterManager.InsertAction(
key = key ?: throw ConcurrentModificationException(),
matter = matter,
complexity = complexity,
priority = priority,
errorOnFailure = errorOnFailure,
replaceIfExists = replaceIfExists,
comparePriority = comparePriority
)
} else {
return MatterManager.InsertAction(
tag = tag ?: throw ConcurrentModificationException(),
matter = matter,
complexity = complexity,
priority = priority,
errorOnFailure = errorOnFailure,
replaceIfExists = replaceIfExists,
comparePriority = comparePriority
)
}
}
}
class DeleteConfiguration(name: ResourceLocation) : Configuration(name) {
override fun build(): MatterManager.DeleteAction {
checkKeyTag()
if (key != null) {
return MatterManager.DeleteAction(
key = key ?: throw ConcurrentModificationException(),
errorOnFailure = errorOnFailure,
)
} else {
return MatterManager.DeleteAction(
tag = tag ?: throw ConcurrentModificationException(),
errorOnFailure = errorOnFailure,
)
}
}
}
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>>()
fun addMatterFunction(function: MatterManager.UpdateAction.Function, value: ImpreciseFraction): UpdateConfiguration {
matterFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value))
matter = null
return this
}
fun addComplexityFunction(function: MatterManager.UpdateAction.Function, value: Double): UpdateConfiguration {
complexityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value))
matter = null
return this
}
fun addPriorityFunction(function: MatterManager.UpdateAction.Function, value: Int): UpdateConfiguration {
priorityFunctions.add(MatterManager.UpdateAction.BoundFunction(function, value))
matter = null
return this
}
override fun build(): MatterManager.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" }
check(
matterFunctions.isNotEmpty() || matter != null ||
complexityFunctions.isNotEmpty() || complexity != null ||
priorityFunctions.isNotEmpty() || priority != null
) { "Update configuration is completely empty" }
checkMatterValues()
checkKeyTag()
if (key != null) {
return MatterManager.UpdateAction(
key = key ?: throw ConcurrentModificationException(),
matter = matter,
complexity = complexity,
priority = priority,
errorOnFailure = errorOnFailure,
matterFunctions = matterFunctions,
complexityFunctions = complexityFunctions,
priorityFunctions = priorityFunctions
)
} else {
return MatterManager.UpdateAction(
tag = tag ?: throw ConcurrentModificationException(),
matter = matter,
complexity = complexity,
priority = priority,
errorOnFailure = errorOnFailure,
matterFunctions = matterFunctions,
complexityFunctions = complexityFunctions,
priorityFunctions = priorityFunctions
)
}
}
}
fun insert(name: ResourceLocation, configurator: InsertConfiguration.() -> Unit): MatterManager.InsertAction {
check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = InsertConfiguration(name)
configurator.invoke(config)
val built = config.build()
actions[name] = built
return built
}
fun delete(name: ResourceLocation, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction {
check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = DeleteConfiguration(name)
configurator.invoke(config)
val built = config.build()
actions[name] = built
return built
}
fun update(name: ResourceLocation, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction {
check(!actions.containsKey(name)) { "Already has action with name $name" }
val config = UpdateConfiguration(name)
configurator.invoke(config)
val built = config.build()
actions[name] = built
return built
}
protected fun updateLocation(input: ResourceLocation, prefix: String): ResourceLocation {
if (namespace != null) {
if (input.namespace == namespace) {
return ResourceLocation(input.namespace, prefix + input.path)
} else {
return ResourceLocation(namespace, prefix + input.namespace + "/" + input.path)
}
} else {
return ResourceLocation(input.namespace, prefix + input.path)
}
}
fun insert(name: ItemLike, configurator: InsertConfiguration.() -> Unit): MatterManager.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 {
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 {
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 {
return insert(updateLocation(name.location, "tag/")) {
tag = name
configurator.invoke(this)
}
}
fun delete(name: TagKey<Item>, configurator: DeleteConfiguration.() -> Unit): MatterManager.DeleteAction {
return delete(updateLocation(name.location, "tag/")) {
tag = name
configurator.invoke(this)
}
}
fun update(name: TagKey<Item>, configurator: UpdateConfiguration.() -> Unit): MatterManager.UpdateAction {
return update(updateLocation(name.location, "tag/")) {
tag = name
configurator.invoke(this)
}
}
fun insert(name: ResourceLocation, configurator: Consumer<InsertConfiguration>) = insert(name, configurator::accept)
fun delete(name: ResourceLocation, configurator: Consumer<DeleteConfiguration>) = delete(name, configurator::accept)
fun update(name: ResourceLocation, configurator: Consumer<UpdateConfiguration>) = update(name, configurator::accept)
fun insert(name: ItemLike, configurator: Consumer<InsertConfiguration>) = insert(name, configurator::accept)
fun delete(name: ItemLike, configurator: Consumer<DeleteConfiguration>) = delete(name, configurator::accept)
fun update(name: ItemLike, configurator: Consumer<UpdateConfiguration>) = update(name, configurator::accept)
fun insert(name: TagKey<Item>, configurator: Consumer<InsertConfiguration>) = insert(name, configurator::accept)
fun delete(name: TagKey<Item>, configurator: Consumer<DeleteConfiguration>) = delete(name, configurator::accept)
fun update(name: TagKey<Item>, configurator: Consumer<UpdateConfiguration>) = update(name, configurator::accept)
/**
* Override this if you prefer The Mundane Way instead of calling [insert], [delete] and [update] directly
*
* Called inside [run]
*/
protected open fun addActions() {}
protected val added = ArrayList<MatterManager.AbstractRegistryAction>()
val addedView: List<MatterManager.AbstractRegistryAction> = Collections.unmodifiableList(added)
final override fun run(output: CachedOutput) {
addActions()
for ((key, value) in actions) {
DataProvider.saveStable(output, value.toJson(), pathProvider.json(key))
added.add(value)
}
}
override fun getName(): String {
return "Matter Data"
}
}

View File

@ -0,0 +1,843 @@
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 it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
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
import net.minecraftforge.event.AddReloadListenerEvent
import net.minecraftforge.registries.ForgeRegistries
import org.apache.logging.log4j.LogManager
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.registryName
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.stream
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 location = json["id"]?.asString?.let { ResourceLocation.tryParse(it) } ?: throw JsonParseException("Invalid `id` value: ${json["id"]}")
if (location.namespace == "#") {
throw JsonParseException("Invalid `id` value: $location")
}
if (location.namespace.startsWith("#")) {
tag = ItemTags.create(ResourceLocation(location.toString().substring(1)))
key = null
} else {
key = location
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 = false,
) : 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 = false,
) : 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 ?: false
}
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" }
checkNotNull(complexity) { "Missing complexity value, which is required for insert action" }
check(!comparePriority || priority != null) { "If compare_priority is true then priority must not be null" }
if (!checkConditions()) {
return
}
if (key != null) {
if (replaceIfExists) {
// search & replace in parallel, if possible
val replaced = registry.parallelStream().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.parallelStream().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.parallelStream().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.parallelStream().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" }
check(complexity == null) { "Delete action can't have complexity value" }
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")
}
if (matter != null && matterFunctions.isNotEmpty()) {
throw JsonParseException("Can't have both matter and matter functions specified at the same time")
}
if (complexity != null && complexityFunctions.isNotEmpty()) {
throw JsonParseException("Can't have both complexity and complexity functions specified at the same time")
}
if (priority != null && priorityFunctions.isNotEmpty()) {
throw JsonParseException("Can't have both priority and priority functions specified at the same time")
}
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,
var complexity: Double,
var priority: Int
) {
abstract fun freeze(): Entry
}
class MutableTagEntry(
val tag: TagKey<Item>,
modificationChain: MutableList<ResourceLocation>,
matter: ImpreciseFraction,
complexity: Double,
priority: Int
) : MutableEntry(modificationChain, matter, complexity, priority) {
override fun freeze(): Entry {
return TagEntry(tag, ImmutableList.copyOf(modificationChain), matter, complexity, priority)
}
}
class MutableKeyEntry(
val key: ResourceLocation,
modificationChain: MutableList<ResourceLocation>,
matter: ImpreciseFraction,
complexity: Double,
priority: Int
) : MutableEntry(modificationChain, matter, complexity, priority) {
override fun freeze(): Entry {
return KeyEntry(key, ImmutableList.copyOf(modificationChain), matter, complexity, priority)
}
}
interface IMatterValue {
val matter: ImpreciseFraction
val complexity: Double
val hasMatterValue: Boolean get() = matter.isPositive && complexity > 0.0
}
sealed class Entry(
val modificationChain: List<ResourceLocation>,
final override val matter: ImpreciseFraction,
final override val complexity: Double,
val priority: Int,
) : IMatterValue {
companion object : Entry(listOf(), ImpreciseFraction.ZERO, 0.0, Int.MIN_VALUE)
}
class TagEntry(
val tag: TagKey<Item>,
modificationChain: List<ResourceLocation>,
matter: ImpreciseFraction,
complexity: Double,
priority: Int,
) : Entry(modificationChain, matter, complexity, priority) {
val bound by lazy {
val manager = ForgeRegistries.ITEMS.tags() ?: throw NullPointerException("Forge registry of Items has no tags!")
manager.getTag(tag)
}
}
class KeyEntry(
val key: ResourceLocation,
modificationChain: List<ResourceLocation>,
matter: ImpreciseFraction,
complexity: Double,
priority: Int,
) : Entry(modificationChain, matter, complexity, priority)
private var canCompute = false
private val keyEntries = HashMap<ResourceLocation, KeyEntry>()
private val tagEntries = ArrayList<TagEntry>()
private val computedEntries = Reference2ObjectOpenHashMap<Item, Entry>()
operator fun get(value: Item): Entry {
check(canCompute) { "Can't compute matter values yet, datapack wasn't loaded or is in progress of loading!" }
synchronized(computedEntries) {
return computedEntries.computeIfAbsent(value, Reference2ObjectFunction {
it as Item
val key = it.registryName ?: throw NullPointerException("$it has no registry name!")
val keyEntry = keyEntries[key]
if (keyEntry != null) {
return@Reference2ObjectFunction keyEntry
}
for (entry in tagEntries) {
if (entry.bound.contains(it)) {
return@Reference2ObjectFunction entry
}
}
return@Reference2ObjectFunction Entry.Companion
})
}
}
fun reloadEvent(event: AddReloadListenerEvent) {
event.addListener(this)
}
private val LOGGER = LogManager.getLogger()
override fun prepare(
resourceManager: ResourceManager,
profilerFiller: ProfilerFiller
): MutableMap<ResourceLocation, JsonElement> {
val time = System.nanoTime()
LOGGER.info("Beginning preparations for loading matter values")
profilerFiller.push("otm_matter_registry_prepare")
try {
val prep = super.prepare(resourceManager, profilerFiller)
val diff = System.nanoTime() - time
LOGGER.info("Finished preparations for loading matter values, took ${diff / 1_000_000L}ms")
return prep
} finally {
profilerFiller.pop()
}
}
override fun apply(
map: MutableMap<ResourceLocation, JsonElement>,
resourceManager: ResourceManager,
profilerFiller: ProfilerFiller
) {
val time = System.nanoTime()
LOGGER.info("Beginning loading matter values")
profilerFiller.push("otm_matter_registry")
try {
canCompute = false
keyEntries.clear()
tagEntries.clear()
computedEntries.clear()
val add = ArrayList<Pair<InsertAction, ResourceLocation>>()
val addOrReplace = ArrayList<Pair<InsertAction, ResourceLocation>>()
val update = ArrayList<Pair<UpdateAction, ResourceLocation>>()
val delete = ArrayList<Pair<DeleteAction, ResourceLocation>>()
for ((key, json) in map) {
if (json !is JsonObject) {
throw JsonParseException("Matter value $key has invalid type: ${json::class.qualifiedName} ($json)")
}
when (val action = json["action"]?.asString?.lowercase() ?: throw JsonParseException("Missing `action` json element in $key. Possible values are: (INSERT, DELETE and UPDATE (case-insensitive))")) {
"insert" -> {
val value = InsertAction(json)
if (value.replaceIfExists) {
addOrReplace.add(value to key)
} else {
add.add(value to key)
}
}
"update" -> update.add(UpdateAction(json) to key)
"delete" -> delete.add(DeleteAction(json) to key)
else -> throw JsonParseException("Unknown action $action of $key")
}
}
val registry = ArrayList<MutableEntry>()
for (action in add)
action.first.update(registry, action.second)
for (action in addOrReplace)
action.first.update(registry, action.second)
for (action in delete)
action.first.update(registry, action.second)
for (action in update)
action.first.update(registry, action.second)
val tags = HashMap<TagKey<Item>, MutableTagEntry>()
val keys = HashMap<ResourceLocation, MutableKeyEntry>()
for (entry in registry) {
if (entry is MutableTagEntry) {
val existing = tags[entry.tag]
if (existing == null || existing.priority < entry.priority) {
tags[entry.tag] = entry
}
} else if (entry is MutableKeyEntry) {
val existing = keys[entry.key]
if (existing == null || existing.priority < entry.priority) {
keys[entry.key] = entry
}
}
}
for (key in keys.values) {
keyEntries[key.key] = key.freeze() as KeyEntry
}
for (tag in tags.values) {
tagEntries.add(tag.freeze() as TagEntry)
}
tagEntries.sortBy { it.priority }
canCompute = true
} finally {
profilerFiller.pop()
}
val diff = System.nanoTime() - time
LOGGER.info("Finished loading matter values, took ${diff / 1_000_000L}ms")
}
}

View File

@ -131,14 +131,14 @@ fun getMatterValue(item: Item): MatterTuple {
if (item is IMatterItem)
return item.getMatterValue(ItemStack.EMPTY) ?: MatterTuple.ZERO
return rootEntries[item] ?: derivedEntries[item] ?: MatterTuple.ZERO
return MatterManager[item].let { MatterTuple(it.matter, it.complexity) }
}
fun getMatterValueNullable(item: Item): MatterTuple? {
if (item is IMatterItem)
return item.getMatterValue(ItemStack.EMPTY)
return rootEntries[item] ?: derivedEntries[item]
return MatterManager[item].let { MatterTuple(it.matter, it.complexity) }
}
fun hasMatterValue(item: Item): Boolean {