Un-tangle matter system

This commit is contained in:
DBotThePony 2022-11-12 23:50:26 +07:00
parent 37e4f33af6
commit 9592860349
Signed by: DBot
GPG Key ID: DCC23B5715498507
23 changed files with 971 additions and 1028 deletions

View File

@ -42,7 +42,6 @@ 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.MatterManager;
import ru.dbotthepony.mc.otm.matter.RecipeResolverManager;
import ru.dbotthepony.mc.otm.network.*;
import ru.dbotthepony.mc.otm.registry.*;
import ru.dbotthepony.mc.otm.storage.*;
@ -121,7 +120,7 @@ public final class OverdriveThatMatters {
var modBus = FMLJavaModLoadingContext.get().getModEventBus();
MRegistry.INSTANCE.initialize(modBus);
RecipeResolverManager.INSTANCE.initialize(modBus);
MatterManager.INSTANCE.initialize(modBus);
modBus.addListener(EventPriority.HIGHEST, this::setup);
modBus.addListener(EventPriority.NORMAL, this::setupClient);
@ -171,8 +170,7 @@ public final class OverdriveThatMatters {
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidResearchManager.INSTANCE::syncEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::reloadEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, RecipeResolverManager.INSTANCE::reloadEvent);
EVENT_BUS.addListener(EventPriority.NORMAL, RecipeResolverManager.INSTANCE::onServerStarted);
EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::onServerStarted);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStopping);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onLevelUnload);

View File

@ -251,7 +251,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Synchro
if (item.item.item === MItems.GRAVITATIONAL_DISRUPTOR) {
collapse()
} else {
val mass = MatterManager.getValue(item.item)
val mass = MatterManager.get(item.item)
if (mass.hasMatterValue)
this.mass += mass.matter

View File

@ -220,7 +220,7 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
copy.count = 1
if (MatterManager.canDecompose(copy)) {
val matter = MatterManager.getValue(copy)
val matter = MatterManager.get(copy)
stack.count--
return DecomposerJob((level?.random?.nextDouble() ?: 1.0) <= 0.2, matter.matter, matter.complexity) to null

View File

@ -194,7 +194,7 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
val graph = matterNode.graph as MatterNetworkGraph? ?: return null to null
val allocation = graph.allocateTask(simulate = false) ?: return null to IdleReason.OBSERVING
val stack = allocation.task.stack(1)
val matter = MatterManager.getValue(stack)
val matter = MatterManager.get(stack)
// ????????
if (!matter.hasMatterValue) return null to null

View File

@ -198,7 +198,7 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
val copy = stack.copy().also { it.count = 1 }
stack.shrink(1)
container.setChanged()
val complexity = MatterManager.getValue(copy).complexity
val complexity = MatterManager.get(copy).complexity
return ItemJob(copy, (if (complexity > 1.0) complexity.pow(2.0) else complexity.pow(0.5)), BASE_CONSUMPTION) to null
}

View File

@ -420,3 +420,11 @@ fun <T> Collection<T>.probablyParallelStream(): Stream<out T> {
}
fun <T> Array<T>.stream(): Stream<T> = Arrays.stream(this)
fun <T> Stream<T?>.filterNotNull(): Stream<T> {
return filter { it != null } as Stream<T>
}
inline fun <reified T> Stream<*>.filterIsInstance(): Stream<T> {
return filter { it is T } as Stream<T>
}

View File

@ -31,18 +31,16 @@ class CreativePatternItem : Item(Properties().rarity(Rarity.EPIC).tab(OverdriveT
private val resolver = LazyOptional.of<IPatternStorage> { this }
override val patterns: Stream<out IPatternState>
get() = MatterManager.resolveAndStream().map { PatternState(UUID(34783464838L, 4463458382L + ForgeRegistries.ITEMS.getID(it.key)), it.key, 1.0) }
get() = MatterManager.valuesList.stream().map { PatternState(UUID(34783464838L, 4463458382L + ForgeRegistries.ITEMS.getID(it.first)), it.first, 1.0) }
override val patternCapacity: Int
get() {
MatterManager.resolveEverything()
return MatterManager.directlyComputedEntriesSize
return MatterManager.valuesList.size
}
override val storedPatterns: Int
get() {
MatterManager.resolveEverything()
return MatterManager.directlyComputedEntriesSize
return MatterManager.valuesList.size
}
override fun insertPattern(

View File

@ -11,7 +11,7 @@ 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 AbstractRegistryAction : Comparable<AbstractRegistryAction> {
sealed class Condition {
abstract fun test(): Boolean
}
@ -120,7 +120,7 @@ sealed class AbstractRegistryAction {
return true
}
abstract fun update(registry: MutableCollection<MatterManager.MutableEntry>, modifier: ResourceLocation)
internal abstract fun update(registry: MutableCollection<MutableEntry>, modifier: ResourceLocation)
protected inline fun fail(reason: () -> String) {
if (errorOnFailure) {
@ -138,6 +138,22 @@ sealed class AbstractRegistryAction {
return other.priority == null
}
override fun compareTo(other: AbstractRegistryAction): Int {
if (other.priority == null) {
if (priority == null) {
return 0
}
return 1
}
if (priority == null) {
return -1
}
return priority.compareTo(other.priority)
}
open fun toJson(): JsonObject {
return JsonObject().also {
if (key != null)

View File

@ -54,8 +54,7 @@ class ComputeAction : AbstractRegistryAction {
}
}
abstract val matter: ImpreciseFraction?
abstract val complexity: Double?
internal abstract fun provideValues(): MatterManager.Result
open fun toJson(): JsonObject {
return JsonObject().also {
@ -64,20 +63,23 @@ class ComputeAction : AbstractRegistryAction {
}
}
fun update(self: IMatterValue): IMatterValue? {
val matter = matter ?: return null
val complexity = complexity ?: return null
internal fun update(self: IMatterValue): MatterManager.Result {
val grab = provideValues()
return MatterValue(
matterFunction.updateValue(self.matter, matter),
complexityFunction.updateValue(self.complexity, complexity)
)
if (grab.value == null) {
return grab
}
return MatterManager.Result(MatterValue(
matterFunction.updateValue(self.matter, grab.value.matter),
complexityFunction.updateValue(self.complexity, grab.value.complexity)
))
}
}
class Constant : Source {
override val matter: ImpreciseFraction
override val complexity: Double
val matter: ImpreciseFraction
val complexity: Double
constructor(
matter: ImpreciseFraction,
@ -103,6 +105,10 @@ class ComputeAction : AbstractRegistryAction {
}
}
override fun provideValues(): MatterManager.Result {
return MatterManager.Result(MatterValue(matter, complexity))
}
override fun toJson(): JsonObject {
return super.toJson().also {
it["type"] = "constant"
@ -135,18 +141,10 @@ class ComputeAction : AbstractRegistryAction {
key = ResourceLocation.tryParse(json["key"]?.asString ?: throw JsonSyntaxException("Missing key")) ?: throw JsonSyntaxException("Invalid key value: ${json["key"]}")
}
// sometimes kotlin compiler is dumb
private fun key() = key
private val matterTuple by lazy {
MatterManager.getValue(ForgeRegistries.ITEMS.getValue(key()) ?: throw NullPointerException("${key()} does not point to anything"))
override fun provideValues(): MatterManager.Result {
return MatterManager.compute(ForgeRegistries.ITEMS.getValue(key) ?: throw NullPointerException("$key does not point to anything"))
}
override val matter: ImpreciseFraction
get() = matterTuple.matter
override val complexity: Double
get() = matterTuple.complexity
override fun toJson(): JsonObject {
return super.toJson().also {
it["type"] = "key"
@ -170,18 +168,10 @@ class ComputeAction : AbstractRegistryAction {
tag = ItemTags.create(ResourceLocation.tryParse(json["tag"]?.asString ?: throw JsonSyntaxException("Missing tag")) ?: throw JsonSyntaxException("Invalid tag value: ${json["tag"]}"))
}
// sometimes kotlin compiler is dumb
private fun tag() = tag
private val matterTuple by lazy {
MatterManager.searchTagEntry(tag())
override fun provideValues(): MatterManager.Result {
return MatterManager.compute(tag)
}
override val matter: ImpreciseFraction?
get() = matterTuple?.matter
override val complexity: Double?
get() = matterTuple?.complexity
override fun toJson(): JsonObject {
return super.toJson().also {
it["type"] = "tag"
@ -238,16 +228,20 @@ class ComputeAction : AbstractRegistryAction {
values = ImmutableList.copyOf(value)
}
private fun values() = values
val computed: IMatterValue? by lazy {
internal fun tryCompute(): MatterManager.Result {
var compute: IMatterValue = IMatterValue.Companion
for (fn in values()) {
compute = fn.update(compute) ?: return@lazy null
for (fn in values) {
val result = fn.update(compute)
if (result.value == null) {
return result
} else {
compute = result.value
}
}
compute
return MatterManager.Result(compute)
}
constructor(json: JsonObject) : super(json) {
@ -280,7 +274,7 @@ class ComputeAction : AbstractRegistryAction {
}
}
override fun update(registry: MutableCollection<MatterManager.MutableEntry>, modifier: ResourceLocation) {
override fun update(registry: MutableCollection<MutableEntry>, modifier: ResourceLocation) {
// do nothing
}
}

View File

@ -25,7 +25,7 @@ class DeleteAction : AbstractRegistryAction {
}
}
override fun update(registry: MutableCollection<MatterManager.MutableEntry>, modifier: ResourceLocation) {
override fun update(registry: MutableCollection<MutableEntry>, modifier: ResourceLocation) {
if (!checkConditions()) {
return
}
@ -37,7 +37,7 @@ class DeleteAction : AbstractRegistryAction {
val iterator = registry.iterator()
for (value in iterator) {
if (value is MatterManager.MutableKeyEntry && value.key == key) {
if (value is MutableKeyEntry && value.key == key) {
iterator.remove()
return
}
@ -48,7 +48,7 @@ class DeleteAction : AbstractRegistryAction {
val iterator = registry.iterator()
for (value in iterator) {
if (value is MatterManager.MutableTagEntry && value.tag == tag) {
if (value is MutableTagEntry && value.tag == tag) {
iterator.remove()
return
}

View File

@ -54,7 +54,7 @@ class InsertAction : AbstractRegistryAction {
}
}
override fun update(registry: MutableCollection<MatterManager.MutableEntry>, modifier: ResourceLocation) {
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)" }
@ -68,7 +68,7 @@ class InsertAction : AbstractRegistryAction {
if (replaceIfExists) {
// search & replace in parallel, if possible
val replaced = registry.probablyParallelStream().anyMatch {
if (it is MatterManager.MutableKeyEntry && it.key == key) {
if (it is MutableKeyEntry && it.key == key) {
if (!comparePriority || it.priority < priority!!) {
it.matter = matter
it.complexity = complexity
@ -85,7 +85,7 @@ class InsertAction : AbstractRegistryAction {
if (!replaced) {
registry.add(
MatterManager.MutableKeyEntry(
MutableKeyEntry(
key,
mutableListOf(modifier),
matter,
@ -95,13 +95,13 @@ class InsertAction : AbstractRegistryAction {
)
}
} else {
if (registry.probablyParallelStream().anyMatch { it is MatterManager.MutableKeyEntry && it.key == key }) {
if (registry.probablyParallelStream().anyMatch { it is MutableKeyEntry && it.key == key }) {
fail { "Value with key $key already exists" }
return
}
registry.add(
MatterManager.MutableKeyEntry(
MutableKeyEntry(
key,
mutableListOf(modifier),
matter,
@ -114,7 +114,7 @@ class InsertAction : AbstractRegistryAction {
if (replaceIfExists) {
// search & replace in parallel, if possible
val replaced = registry.probablyParallelStream().anyMatch {
if (it is MatterManager.MutableTagEntry && it.tag == tag) {
if (it is MutableTagEntry && it.tag == tag) {
if (!comparePriority || it.priority < priority!!) {
it.matter = matter
it.complexity = complexity
@ -131,7 +131,7 @@ class InsertAction : AbstractRegistryAction {
if (!replaced) {
registry.add(
MatterManager.MutableTagEntry(
MutableTagEntry(
tag!!,
mutableListOf(modifier),
matter,
@ -141,13 +141,13 @@ class InsertAction : AbstractRegistryAction {
)
}
} else {
if (registry.probablyParallelStream().anyMatch { it is MatterManager.MutableTagEntry && it.tag == tag }) {
if (registry.probablyParallelStream().anyMatch { it is MutableTagEntry && it.tag == tag }) {
fail { "Value with tag $tag already exists" }
return
}
registry.add(
MatterManager.MutableTagEntry(
MutableTagEntry(
tag!!,
mutableListOf(modifier),
matter,

View File

@ -17,7 +17,7 @@ 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 pathProvider: DataGenerator.PathProvider = dataGenerator.createPathProvider(DataGenerator.Target.DATA_PACK, MatterManager.MATTER_DIRECTORY)
protected val actions = LinkedHashMap<ResourceLocation, AbstractRegistryAction>()
sealed class Configuration(val name: ResourceLocation) {

File diff suppressed because it is too large Load Diff

View File

@ -39,609 +39,3 @@ import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
import java.util.stream.Stream
/**
* This manager holds json defined recipe resolvers (they process their own)
*/
object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(), "otm_recipe_resolver") {
/**
* because it is immutable
*/
data class ImmutableStack(
val item: Item,
val multiplier: Double = 1.0
) {
constructor(item: Item, count: Int) : this(item, 1.0 / count.toDouble())
constructor(item: ItemStack) : this(item.item, item.count)
}
class ResolvedRecipe(
inputs: Stream<Stream<ImmutableStack>>,
val output: ImmutableStack,
val transformMatterValue: (ImpreciseFraction) -> ImpreciseFraction = { it },
val transformComplexity: (Double) -> Double = { it },
/**
* if we can't resolve one of ingredient's matter value - consider output also having no matter value
*/
val isCritical: Boolean = true,
val name: ResourceLocation? = null,
/**
* Whenever to allow "backtracking" recipe ingredients to recipe outputs.
*
* When an item has no recipes, but takes part in recipes with output of already determined matter value
* (e.g. 4 rabbit hides -> 1 leather), we can assume that we can determine matter value of ingredients based on matter value of output
*
* "Backtrack" can happen if everything of the next holds true:
* * All recipes with item in question contains only that item; and
* * All recipes allow backtracking
*/
val allowBacktrack: Boolean = true,
) {
val inputs: List<List<ImmutableStack>> = inputs
.map { it.filter { it.multiplier > 0.0 }.collect(ImmutableList.toImmutableList()) }
.filter { it.isNotEmpty() }
.collect(ImmutableList.toImmutableList())
constructor(
inputs: Collection<Collection<ImmutableStack>>,
output: ImmutableStack,
transformMatterValue: (ImpreciseFraction) -> ImpreciseFraction = { it },
transformComplexity: (Double) -> Double = { it },
) : this(inputs.stream().map { it.stream() }, output, transformMatterValue, transformComplexity)
val formattedName: String
get() = if (name == null) "One of recipes" else "Recipe '$name'"
}
fun interface Finder {
/**
* Returned stream can be either parallel or sequential
*
* Parallel streams will _greatly_ improve performance on mid-end range computers,
* but you need to make sure your stream has no side effects (such as binding late-binding tags)
*
* Internally, OTM separate parallel streams from sequential so sequential streams happen strictly on server thread
* (if you can't avoid side effects, or don't bother with synchronization).
*
* You can safely call [MatterManager.getDirect] inside returned stream, both parallel and sequential.
*/
fun find(server: MinecraftServer, json: JsonObject): Stream<ResolvedRecipe>
}
private val delegate = RegistryDelegate<Finder>("recipe_resolver") {
disableSync()
disableSaving()
}
private val registrar = DeferredRegister.create(delegate.key, OverdriveThatMatters.MOD_ID)
private val LOGGER = LogManager.getLogger()
init {
registrar.register("simple") {
Finder { server, data ->
val location = (data["recipe_type"] ?: throw JsonSyntaxException("Missing recipe type")).let { ResourceLocation.tryParse(it.asString) } ?: throw JsonSyntaxException("Invalid recipe type: ${data["recipe_type"]}")
if (!ForgeRegistries.RECIPE_TYPES.containsKey(location)) {
LOGGER.error("Invalid or missing recipe category: $location!")
return@Finder Stream.empty()
}
val findRecipeType = ForgeRegistries.RECIPE_TYPES.getValue(location) as RecipeType<Recipe<Container>>? ?: throw ConcurrentModificationException()
val isCritical = data["is_critical"]?.asBoolean ?: true
val ignoreDamageables = data["ignore_damageables"]?.asBoolean ?: false
val allowBacktrack = data["allow_backtrack"]?.asBoolean ?: true
var stream = server.recipeManager.byType(findRecipeType).values.parallelStream().filter { !it.isIncomplete }
if (ignoreDamageables) {
stream = stream.filter { it.ingredients.stream().flatMap { it.items.stream() }.noneMatch { it.isDamageableItem } }
}
stream.map {
ResolvedRecipe(
it.ingredients.stream()
.filter { !it.isActuallyEmpty }
.map { it.items.stream().map(::ImmutableStack) },
ImmutableStack(it.resultItem),
isCritical = isCritical,
name = it.id,
allowBacktrack = allowBacktrack
)
}
.filter {
if (it.inputs.isEmpty()) {
LOGGER.warn("${it.formattedName} with output '${it.output.item.registryName}' ended up with no inputs!")
false
} else {
true
}
}
}
}
}
fun initialize(bus: IEventBus) {
bus.addListener(delegate::build)
registrar.register(bus)
}
fun reloadEvent(event: AddReloadListenerEvent) {
event.addListener(this)
}
fun onServerStarted(event: ServerStartedEvent) {
resolve(event.server)
}
@JvmStatic val RESOLVERS get() = delegate.get()
@JvmStatic val RESOLVERS_KEY get() = delegate.key
/**
* it exists solely for fail-fast to find bugs, do not rely on this property
*/
var ready = false
private set
var resolved = false
private set
private var foundResolvers: Map<ResourceLocation, Pair<Finder, JsonObject>> = ImmutableMap.of()
override fun prepare(
p_10771_: ResourceManager,
p_10772_: ProfilerFiller
): MutableMap<ResourceLocation, JsonElement> {
ready = false
resolved = false
// determinedValues.clear() // can't clear here, it is executed on separate thread
return super.prepare(p_10771_, p_10772_)
}
override fun apply(
map: Map<ResourceLocation, JsonElement>,
resourceManager: ResourceManager,
profilerFiller: ProfilerFiller
) {
ready = false
resolved = false
determinedValues.clear()
commentary.clear()
val builder = ImmutableMap.Builder<ResourceLocation, Pair<Finder, JsonObject>>()
for ((key, json) in map) {
if (json !is JsonObject) {
throw JsonParseException("$key is not a json object")
}
val location = (json["type"] ?: throw JsonSyntaxException("Missing resolver type")).let { ResourceLocation.tryParse(it.asString) } ?: throw JsonSyntaxException("Invalid resolver type: ${json["type"]}")
if (!RESOLVERS.containsKey(location)) {
throw JsonParseException("Resolver type $location does not exist (in $key)")
}
val resolver = RESOLVERS.getValue(location) ?: throw ConcurrentModificationException()
builder.put(key, resolver to json)
}
foundResolvers = builder.build()
ready = true
}
private data class Accumulator(
val input2Recipes: Reference2ObjectOpenHashMap<Item, ReferenceOpenHashSet<ResolvedRecipe>> = Reference2ObjectOpenHashMap(),
val output2Recipes: Reference2ObjectOpenHashMap<Item, ReferenceOpenHashSet<ResolvedRecipe>> = Reference2ObjectOpenHashMap(),
) {
val heuristicsSize: Long
get() {
return input2Recipes.size.toLong() + output2Recipes.size.toLong()
}
fun combine(other: Accumulator) {
for ((k, v) in other.input2Recipes) {
val existing = input2Recipes[k]
if (existing == null) {
input2Recipes[k] = v
} else {
existing.addAll(v)
}
}
for ((k, v) in other.output2Recipes) {
val existing = output2Recipes[k]
if (existing == null) {
output2Recipes[k] = v
} else {
existing.addAll(v)
}
}
}
fun add(recipe: ResolvedRecipe) {
output2Recipes.computeIfAbsent(recipe.output.item, Reference2ObjectFunction { ReferenceOpenHashSet() }).add(recipe)
for (input1 in recipe.inputs) {
for (input in input1) {
input2Recipes.computeIfAbsent(input.item, Reference2ObjectFunction { ReferenceOpenHashSet() }).add(recipe)
}
}
}
}
fun getDetermined(index: Item): IMatterValue {
return determinedValues[index] ?: IMatterValue.Companion
}
private var input2Recipes: Map<Item, Collection<ResolvedRecipe>> = mapOf()
private var output2Recipes: Map<Item, Collection<ResolvedRecipe>> = mapOf()
private val determinedValues = Reference2ObjectOpenHashMap<Item, IMatterValue>()
private val commentary = Reference2ObjectOpenHashMap<Item, Component>()
private val seenItems = ArrayDeque<Item>()
fun getCommentary(item: Item): MutableComponent? {
return commentary[item]?.copy()
}
private data class Result(val type: Type, val value: IMatterValue? = null) {
constructor(value: IMatterValue) : this(Type.RESOLVED, value)
val isSkipped get() = type === Type.SKIPPED
val isMissing get() = type === Type.MISSING
enum class Type {
RESOLVED, SKIPPED, MISSING
}
companion object {
// recursive recipes should be ignored until we resolve everything else
val SKIPPED = Result(Type.SKIPPED)
// missing matter values are fatal, whole recipe chain is then considered defunct
val MISSING = Result(Type.MISSING)
}
}
private var changes = false
private var cachedIterationResults = Reference2ObjectOpenHashMap<Item, Result>()
private fun doTryToBacktrack(item: Item, makeCommentary: Boolean): Result {
val recipes = input2Recipes[item]
if (recipes == null || recipes.isEmpty() || !recipes.all { it.allowBacktrack } || !recipes.all { it.inputs.all { it.all { it.item == item } } }) {
if (makeCommentary)
commentary[item] = TextComponent("Item '${item.registryName}' has no recipes")
return Result.MISSING
}
var minimal: IMatterValue? = null
var minimalMultiplier = 0.0
var foundRecipe: ResolvedRecipe? = null
for (recipe in recipes) {
val value = doDetermineValue(recipe.output.item)
if (value.value != null) {
if (minimal == null || minimal < value.value) {
minimal = value.value
minimalMultiplier = recipe.output.multiplier / recipe.inputs.size
foundRecipe = recipe
}
} else if (!value.isSkipped) {
LOGGER.error("${item.registryName} cant resolve ${recipe.output.item}")
minimal = null
break
}
}
if (minimal == null) {
if (makeCommentary)
commentary[item] = TextComponent("Item '${item.registryName}' has no valid backtracking recipes, and no output recipes")
return Result.MISSING
}
val result = MatterValue(minimal.matter * minimalMultiplier, minimal.complexity * minimalMultiplier)
changes = true
determinedValues[item] = result
commentary[item] = TextComponent("Matter value backtracked from ${foundRecipe!!.formattedName}")
return Result(result)
}
private fun tryToBacktrack(item: Item, makeCommentary: Boolean): Result {
val getResult = cachedIterationResults[item]
if (getResult != null) {
return getResult
}
var value: IMatterValue? = MatterManager.getDirect(item)
if (value?.hasMatterValue == true) {
return Result(value)
}
value = determinedValues[item]
if (value?.hasMatterValue == true) {
return Result(value)
}
if (item in seenItems && item != seenItems.last()) {
return Result.SKIPPED
}
seenItems.addLast(item)
try {
val result = doTryToBacktrack(item, makeCommentary)
cachedIterationResults[item] = result
return result
} finally {
seenItems.removeLast()
}
}
private fun doDetermineValue(item: Item): Result {
var minimalMatter: ImpreciseFraction? = null
var minimalComplexity: Double? = null
val recipes = output2Recipes[item]
if (recipes == null || recipes.isEmpty()) {
return tryToBacktrack(item, true)
}
var hadSkips = false
var minimalRecipe: ResolvedRecipe? = null
recipesLoop@ for (recipe in recipes) {
if (recipe.inputs.isEmpty()) {
// TODO: should we ignore empty recipes?
commentary[item] = TextComponent("${recipe.formattedName} is empty")
continue
}
var accumulatedMatter: ImpreciseFraction? = null
var accumulatedComplexity: Double? = null
inputsLoop@ for ((i, inputs) in recipe.inputs.withIndex()) {
var minimal: IMatterValue? = null
var minimalMultiplier = 0.0
for (input in inputs) {
val ivalue = determineValue(input.item)
if (ivalue.isMissing) {
commentary[item] = TextComponent("Input '${input.item.registryName}' at input slot $i in ${recipe.formattedName} has no matter value")
if (recipe.isCritical) {
return Result.MISSING
} else {
continue@recipesLoop
}
} else if (ivalue.isSkipped) {
commentary[item] = TextComponent("Input '${input.item.registryName}' at input slot $i in ${recipe.formattedName} is recursive")
hadSkips = true
continue@recipesLoop
}
if (minimal == null || minimal > ivalue.value!!) {
minimal = ivalue.value
minimalMultiplier = input.multiplier
}
}
if (minimal == null || !minimal.hasMatterValue) {
commentary[item] = TextComponent("'${recipe.formattedName}' has invalid input at slot $i (possible inputs: ${inputs.joinToString(", ", transform = { it.item.registryName.toString() }) }) (???)")
return Result.MISSING
}
if (accumulatedMatter == null || accumulatedComplexity == null) {
accumulatedMatter = minimal.matter * minimalMultiplier
accumulatedComplexity = minimal.complexity * minimalMultiplier
} else {
accumulatedMatter += minimal.matter * minimalMultiplier
accumulatedComplexity += minimal.complexity * minimalMultiplier
}
}
if (accumulatedMatter == null || accumulatedComplexity == null) {
throw RuntimeException("This piece should be unreachable")
}
if (!accumulatedMatter.isPositive || accumulatedComplexity <= 0.0) {
commentary[item] = TextComponent("${recipe.formattedName} ended up with negative matter value and/or complexity (???)")
return Result.MISSING
}
if (minimalMatter == null || minimalMatter > accumulatedMatter) {
minimalMatter = recipe.transformMatterValue(accumulatedMatter * recipe.output.multiplier)
minimalComplexity = recipe.transformComplexity(accumulatedComplexity * recipe.output.multiplier)
minimalRecipe = recipe
}
}
if (minimalMatter == null || minimalComplexity == null) {
if (hadSkips) {
val backtrack = tryToBacktrack(item, false)
if (backtrack.value == null) {
return Result.SKIPPED
} else {
return backtrack
}
}
if (item !in commentary)
commentary[item] = TextComponent("'${item.registryName}' ended up with no valid recipes (???)")
return Result.MISSING
}
val result = MatterValue(minimalMatter, minimalComplexity)
changes = true
determinedValues[item] = result
commentary[item] = TextComponent("Matter value derived from ${minimalRecipe!!.formattedName}")
return Result(result)
}
private fun determineValue(item: Item): Result {
val getResult = cachedIterationResults[item]
if (getResult != null) {
return getResult
}
var value: IMatterValue? = MatterManager.getDirect(item)
if (value?.hasMatterValue == true) {
return Result(value)
}
value = determinedValues[item]
if (value?.hasMatterValue == true) {
return Result(value)
}
if (item in seenItems) {
return Result.SKIPPED
}
seenItems.addLast(item)
try {
val result = doDetermineValue(item)
cachedIterationResults[item] = result
return result
} finally {
seenItems.removeLast()
}
}
private fun resolve(server: MinecraftServer) {
if (resolved) {
return
}
check(ready) { "RecipeResolverManager is not ready to resolve matter values" }
check(MatterManager.canCompute) { "MatteryManager is not ready to compute matter values" }
var time = SystemTime()
LOGGER.info("Finding recipes...")
determinedValues.clear()
seenItems.clear()
commentary.clear()
val foundStreams = foundResolvers.map {
try {
it.value.first.find(server, it.value.second)
} catch(err: Throwable) {
LOGGER.fatal("Recipe resolver ${it.key} experienced internal error", err)
throw RuntimeException("Recipe resolver ${it.key} experienced internal error", err)
}
}
val sequentialStreams = foundStreams.filter { !it.isParallel }
val parallelStreams = foundStreams.filter { it.isParallel }
val streamA = Streams.concat(*sequentialStreams.toTypedArray())
val streamB = Streams.concat(*parallelStreams.toTypedArray())
val resultA = streamA.collect(::Accumulator, Accumulator::add, Accumulator::combine)
val resultB = streamB.collect(::Accumulator, Accumulator::add, Accumulator::combine)
val result: Accumulator
if (resultA.heuristicsSize > resultB.heuristicsSize) {
result = resultA
resultA.combine(resultB)
} else {
result = resultB
resultB.combine(resultA)
}
val (input2Recipes, output2Recipes) = result
LOGGER.info("Finding recipes took ${time.millis}ms, involving ${input2Recipes.keys.size} unique inputs and ${output2Recipes.keys.size} unique outputs")
LOGGER.info("Resolving recipes...")
this.input2Recipes = input2Recipes
this.output2Recipes = output2Recipes
changes = true
var i = 0
var ops = 0
val toDetermine = ReferenceLinkedOpenHashSet<Item>()
toDetermine.addAll(input2Recipes.keys)
toDetermine.addAll(output2Recipes.keys)
if (LOGGER.isDebugEnabled) {
LOGGER.debug("Gonna try to search for matter values for next items:")
val names = ArrayList<String>()
var length = 0
for (value in toDetermine) {
val name = value.registryName!!.toString()
if (length != 0 && length + name.length < 400) {
names.add(name)
length += name.length
} else if (length == 0) {
names.add(name)
length = name.length
} else {
LOGGER.debug(names.joinToString(", "))
names.clear()
length = 0
}
}
if (names.isNotEmpty()) {
LOGGER.debug(names.joinToString(", "))
}
}
time = SystemTime()
while (changes) {
ops += cachedIterationResults.size
cachedIterationResults = Reference2ObjectOpenHashMap()
changes = false
i++
val iterator = toDetermine.iterator()
for (value in iterator) {
if (determineValue(value).value?.hasMatterValue == true) {
iterator.remove()
}
}
}
for (item in toDetermine) {
if (item !in commentary) {
commentary[item] = TextComponent("Item ${item.registryName} was never visited")
}
}
resolved = true
LOGGER.info("Resolving recipes took ${time.millis}ms in $i iterations with ~$ops operations, determined ${determinedValues.size} matter values")
}
}

View File

@ -123,7 +123,7 @@ class UpdateAction : AbstractRegistryAction {
}
}
fun apply(value: MatterManager.MutableEntry, modifier: ResourceLocation) {
private fun apply(value: MutableEntry, modifier: ResourceLocation) {
if (matterFunctions.isNotEmpty())
value.matter = matterFunctions.apply(value.matter)
else if (matter != null)
@ -142,7 +142,7 @@ class UpdateAction : AbstractRegistryAction {
value.modificationChain.add(modifier)
}
override fun update(registry: MutableCollection<MatterManager.MutableEntry>, modifier: ResourceLocation) {
override fun update(registry: MutableCollection<MutableEntry>, modifier: ResourceLocation) {
if(!(
matter != null ||
complexity != null ||
@ -174,7 +174,7 @@ class UpdateAction : AbstractRegistryAction {
val iterator = registry.iterator()
for (value in iterator) {
if (value is MatterManager.MutableKeyEntry && value.key == key) {
if (value is MutableKeyEntry && value.key == key) {
apply(value, modifier)
return
}
@ -185,7 +185,7 @@ class UpdateAction : AbstractRegistryAction {
val iterator = registry.iterator()
for (value in iterator) {
if (value is MatterManager.MutableTagEntry && value.tag == tag) {
if (value is MutableTagEntry && value.tag == tag) {
apply(value, modifier)
return
}