diff --git a/build.gradle.kts b/build.gradle.kts index dfcea109e..361d035c9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -201,12 +201,12 @@ dependencies { compileOnly(fg.deobf("mekanism:Mekanism:${deps_mc_version}-${mekanism_version}:all")) - runtimeOnly(fg.deobf("curse.maven:cyclops-core-232758:4392602")) - runtimeOnly(fg.deobf("curse.maven:integrated-dynamics-236307:4391535")) - runtimeOnly(fg.deobf("curse.maven:integrated-crafting-287357:4391487")) - runtimeOnly(fg.deobf("curse.maven:integrated-terminals-295910:4400924")) - runtimeOnly(fg.deobf("curse.maven:common-capabilities-247007:4391468")) - runtimeOnly(fg.deobf("curse.maven:integrated-tunnels-251389:4344632")) + // runtimeOnly(fg.deobf("curse.maven:cyclops-core-232758:4392602")) + // runtimeOnly(fg.deobf("curse.maven:integrated-dynamics-236307:4391535")) + // runtimeOnly(fg.deobf("curse.maven:integrated-crafting-287357:4391487")) + // runtimeOnly(fg.deobf("curse.maven:integrated-terminals-295910:4400924")) + // runtimeOnly(fg.deobf("curse.maven:common-capabilities-247007:4391468")) + // runtimeOnly(fg.deobf("curse.maven:integrated-tunnels-251389:4344632")) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt index c9839c011..3c93b2f80 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/MatterManager.kt @@ -14,6 +14,7 @@ import it.unimi.dsi.fastutil.io.FastByteArrayInputStream import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import it.unimi.dsi.fastutil.objects.Object2BooleanFunction import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap import it.unimi.dsi.fastutil.objects.Reference2BooleanFunction import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction @@ -88,6 +89,7 @@ import ru.dbotthepony.mc.otm.milliTime import ru.dbotthepony.mc.otm.network.MatteryPacket import ru.dbotthepony.mc.otm.network.RegistryNetworkChannel import ru.dbotthepony.mc.otm.registry.RegistryDelegate +import ru.dbotthepony.mc.otm.secondTime import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import java.io.DataInputStream import java.io.DataOutputStream @@ -105,6 +107,7 @@ import kotlin.collections.ArrayDeque import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.LinkedHashMap +import kotlin.jvm.optionals.getOrNull import kotlin.math.pow import kotlin.math.roundToInt @@ -596,6 +599,8 @@ object MatterManager { private val seenItems = ArrayDeque() private var changes = false + private var iteration = 0 + private var lenientStage = false private var cachedIterationResults = Reference2ObjectOpenHashMap() private fun doTryToBacktrack(item: Item, makeCommentary: Boolean): Result { @@ -708,6 +713,8 @@ object MatterManager { inputsLoop@ for ((i, inputs) in recipe.inputs.withIndex()) { var minimal: IMatterValue? = null var minimalMultiplier = 0.0 + val skips = ArrayList() + val recursiveSkips = ArrayList() innerInputsLoop@ for (input in inputs) { val ivalue = determineValue(input.item) @@ -715,13 +722,19 @@ object MatterManager { if (ivalue.isMissing) { comment(item, TextComponent("Input '${input.item.registryName}' at input slot $i in ${recipe.formattedName} has no matter value")) - if (recipe.isCritical) { + if (recipe.isCritical && !lenientStage) { return Result.MISSING } else { - continue@recipesLoop + if (recipe.isCritical) { + skips.add(input) + continue@innerInputsLoop + } else { + continue@recipesLoop + } } } else if (ivalue.isSkipped) { comment(item, TextComponent("Input '${input.item.registryName}' at input slot $i in ${recipe.formattedName} is recursive")) + recursiveSkips.add(input) if (inputs.size == 1) { hadSkips = true @@ -737,6 +750,53 @@ object MatterManager { } } + if (skips.size > inputs.size / 2 || skips.isNotEmpty() && inputs.size == 1) { + comment(item, TextComponent("More than half inputs (${skips.joinToString(", ") { it.item.registryName.toString() }}) at input slot $i in ${recipe.formattedName} have no matter values")) + return Result.MISSING + } else if (skips.isNotEmpty()) { + /** + * Эвристический анализ тегов + * + * Если у 60% <= предметов со значением материи есть тег, и он есть у всех + * предметов без значения материи, то предметы без материи игнорируются + */ + val manager = ForgeRegistries.ITEMS.tags()!! + val tagSetsPresent = Object2IntArrayMap>() + val tagSetsMissing = Object2IntArrayMap>() + + for (input in inputs) { + if (input in recursiveSkips) { + continue + } + + val list = manager.getReverseTag(input.item).getOrNull()?.tagKeys?.collect(ImmutableList.toImmutableList()) ?: ImmutableList.of() + + if (input !in skips) { + for (tag in list) + tagSetsPresent.computeInt(tag) { _, e -> (e ?: 0) + 1 } + } else { + for (tag in list) + tagSetsMissing.computeInt(tag) { _, e -> (e ?: 0) + 1 } + } + } + + val filtered = tagSetsPresent.object2IntEntrySet() + .filter { it.intValue.toDouble() >= (inputs.size - skips.size - recursiveSkips.size) * 0.6 } + + val result = filtered.all { tagSetsMissing.getInt(it.key) == skips.size } + + if (!result) { + comment(item, TextComponent("More than half inputs (${skips.joinToString(", ") { it.item.registryName.toString() }}) at input slot $i in ${recipe.formattedName} have no matter values")) + return Result.MISSING + } else { + comment(item, TextComponent("${recipe.formattedName} with inputs at slot $i without matter values were allowed to be skipped due to next tags:")) + + for (tag in filtered) { + comment(item, TextComponent(tag.key.location.toString())) + } + } + } + if (minimal == null || !minimal.hasMatterValue) { comment(item, TextComponent("'${recipe.formattedName}' has invalid input at slot $i (possible inputs: ${inputs.joinToString(", ", transform = { it.item.registryName.toString() }) }) (???)")) return Result.MISSING @@ -907,6 +967,26 @@ object MatterManager { time = SystemTime() + lenientStage = false + + while (changes) { + ops += cachedIterationResults.size + cachedIterationResults = Reference2ObjectOpenHashMap() + changes = false + iteration++ + + val iterator = toDetermine.iterator() + + for (value in iterator) { + if (determineValue(value).value?.hasMatterValue == true) { + iterator.remove() + } + } + } + + lenientStage = true + changes = true + while (changes) { ops += cachedIterationResults.size cachedIterationResults = Reference2ObjectOpenHashMap() @@ -936,8 +1016,6 @@ object MatterManager { } } - var iteration = 0 - internal fun compute(value: Item): Result { return Resolver.determineValue(value) } @@ -1132,11 +1210,20 @@ object MatterManager { if (commentary != null) { if (commentary.size > 3) { - event.toolTip.add(TextComponent("...").withStyle(ChatFormatting.DARK_GRAY)) - } + var index = ((secondTime / 3L) % commentary.size).toInt() - for (i in (commentary.size - 3).coerceAtLeast(0) until commentary.size) { - event.toolTip.add(commentary[i].withStyle(ChatFormatting.DARK_GRAY)) + for (i in 0 .. 2) { + if (index == commentary.size) { + event.toolTip.add(TextComponent("=========").withStyle(ChatFormatting.DARK_GRAY)) + index = 0 + } + + event.toolTip.add(commentary[index++].withStyle(ChatFormatting.DARK_GRAY)) + } + } else { + for (comment in commentary) { + event.toolTip.add(comment.withStyle(ChatFormatting.DARK_GRAY)) + } } } }