diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleGeneratorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleGeneratorBlockEntity.kt index 3318c44a4..5068bc539 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleGeneratorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleGeneratorBlockEntity.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.block.entity.blackhole import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArraySet -import it.unimi.dsi.fastutil.objects.ObjectIterators import net.minecraft.core.BlockPos import net.minecraft.network.chat.Component import net.minecraft.world.entity.player.Inventory @@ -10,6 +9,7 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.level.block.state.BlockState import net.neoforged.neoforge.capabilities.Capabilities +import ru.dbotthepony.kommons.collect.toList import ru.dbotthepony.kommons.util.DelegateSetter import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.setValue @@ -23,7 +23,6 @@ import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.matter.CombinedProfiledMatterStorage import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.ServerConfig -import ru.dbotthepony.mc.otm.core.RandomSource2Generator import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.multiblock.ShapedMultiblock @@ -34,6 +33,7 @@ import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock +import ru.dbotthepony.mc.otm.core.util.InvalidableLazy import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockTags @@ -44,6 +44,7 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) MBlockEntities.BLACK_HOLE_GENERATOR, blockPos, blockState) { var multiblock: ShapedMultiblock? = null private set + private var multiblockSyncher: Closeable? = null var lastRange by syncher.int(-1, setter = DelegateSetter { field, value -> @@ -51,13 +52,23 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) multiblockSyncher?.close() multiblockSyncher = null multiblock?.close() + energyTarget.invalidate() + matterTarget.invalidate() multiblock = null field.accept(value) } else if (value != field.value) { multiblockSyncher?.close() multiblock?.close() multiblock = CONFIGURATIONS[value - 5].value.create(blockPos) + + multiblock!!.onGenerationChanges(Runnable { + energyTarget.invalidate() + matterTarget.invalidate() + }) + multiblockSyncher = syncher.add0(multiblock!!) + energyTarget.invalidate() + matterTarget.invalidate() field.accept(value) } }) @@ -65,17 +76,16 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) var drawBuildingGuide by syncher.boolean() - val energy = CombinedProfiledEnergyStorage( - FlowDirection.NONE, - { multiblock?.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)?.iterator()?.map { it.energy } ?: ObjectIterators.emptyIterator() }, - { level?.random } - ) + private val energyTarget = InvalidableLazy.Impl { + multiblock?.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)?.iterator()?.map { it.energy }?.toList() ?: listOf() + } - val matter = CombinedProfiledMatterStorage( - FlowDirection.NONE, - { multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter } ?: ObjectIterators.emptyIterator() }, - { level?.random } - ) + private val matterTarget = InvalidableLazy.Impl { + multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter }?.toList() ?: listOf() + } + + val energy = CombinedProfiledEnergyStorage(FlowDirection.NONE, energyTarget::value, { level?.random }) + val matter = CombinedProfiledMatterStorage(FlowDirection.NONE, matterTarget::value, { level?.random }) enum class Mode(val label: Component, val tooltip: Component) { TARGET_MASS(TranslatableComponent("otm.gui.black_hole_generator.sustain.mode"), TranslatableComponent("otm.gui.black_hole_generator.sustain.desc")), diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyInterfaceBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyInterfaceBlockEntity.kt index 0dc83d27e..b4e300552 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyInterfaceBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyInterfaceBlockEntity.kt @@ -11,6 +11,8 @@ import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.energy.IEnergyStorage import ru.dbotthepony.kommons.collect.flatMap import ru.dbotthepony.kommons.collect.map +import ru.dbotthepony.kommons.collect.toList +import ru.dbotthepony.kommons.util.Listenable import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage @@ -22,6 +24,7 @@ import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockListener +import ru.dbotthepony.mc.otm.core.util.InvalidableLazy import ru.dbotthepony.mc.otm.registry.game.MBlockEntities class EnergyInterfaceBlockEntity( @@ -34,20 +37,22 @@ class EnergyInterfaceBlockEntity( val energyInterfaceTarget: ProfiledEnergyStorage<*> } - private val multiblocks = LinkedHashSet() + private val multiblocks = LinkedHashMap() + private val targets = InvalidableLazy.Impl { + multiblocks.keys.iterator().flatMap { it.blockEntities(TARGET).iterator() }.map { it.energyInterfaceTarget }.toList() + } - val energy = CombinedProfiledEnergyStorage( - FlowDirection.input(isInput), - { multiblocks.iterator().flatMap { it.blockEntities(TARGET).iterator() }.map { it.energyInterfaceTarget } }, - { level?.random } - ) + val energy = CombinedProfiledEnergyStorage(FlowDirection.input(isInput), targets::value, { level?.random }) override fun onAddedToMultiblock(multiblock: IMultiblockAccess) { - check(multiblocks.add(multiblock)) { "$this already tracks $multiblock" } + check(!isRemoved) { "Block was removed" } + check(multiblocks.put(multiblock, multiblock.onGenerationChanges(Runnable { targets.invalidate() })) == null) { "$this already tracks $multiblock" } + targets.invalidate() } override fun onRemovedFromMultiblock(multiblock: IMultiblockAccess) { - check(multiblocks.remove(multiblock)) { "$this does not track $multiblock" } + check(multiblocks.remove(multiblock)?.remove() != null || isRemoved) { "$this does not track $multiblock" } + targets.invalidate() } val container = object : MatteryContainer(::markDirtyFast, CAPACITY) { @@ -59,6 +64,11 @@ class EnergyInterfaceBlockEntity( val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable) private val neighbours = ArrayList>() + override fun setRemoved() { + super.setRemoved() + multiblocks.values.forEach { it.remove() } + } + init { savetables.stateful(::container, BATTERY_KEY) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt index e1ee4df84..d2ac47101 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/Ext.kt @@ -1,10 +1,8 @@ package ru.dbotthepony.mc.otm.capability import com.google.common.collect.Streams -import it.unimi.dsi.fastutil.objects.ObjectArrayList import net.minecraft.ChatFormatting import net.minecraft.network.chat.Component -import net.minecraft.util.RandomSource import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.player.Player import net.minecraft.world.item.ItemStack @@ -35,10 +33,7 @@ import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.math.Decimal -import ru.dbotthepony.mc.otm.core.shuffle import ru.dbotthepony.mc.otm.core.util.formatFluidLevel -import java.util.function.Supplier -import java.util.random.RandomGenerator import java.util.stream.Stream private val LOGGER = LogManager.getLogger() @@ -46,37 +41,6 @@ private val LOGGER = LogManager.getLogger() val Player.matteryPlayer: MatteryPlayer get() = (this as IMatteryPlayer).otmPlayer val LivingEntity.matteryPlayer: MatteryPlayer? get() = (this as? IMatteryPlayer)?.otmPlayer -fun Supplier>.iterateProviders(random: RandomSource?, value: Decimal, simulate: Boolean, walker: T.(Decimal, Boolean) -> Decimal): Decimal { - val providers = get() - val iteratorProvider: () -> Iterator - - if (random == null) { - iteratorProvider = providers::iterator - } else { - val providerList = ObjectArrayList(providers) - providerList.shuffle(random) - iteratorProvider = providerList::iterator - } - - var leftover = value - - for (provider in iteratorProvider()) { - leftover -= walker(provider, leftover, true) - if (leftover.signum() <= 0) break - } - - if (simulate) return value - leftover - - leftover = value - - for (provider in iteratorProvider()) { - leftover -= walker(provider, leftover, false) - if (leftover.signum() <= 0) break - } - - return value - leftover -} - /** * Does a checked energy receive, calls [IMatteryEnergyStorage.receiveEnergyChecked] if possible */ diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedEnergyStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedEnergyStorage.kt index 1f0a0ac21..fcdeacfb6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedEnergyStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedEnergyStorage.kt @@ -2,33 +2,37 @@ package ru.dbotthepony.mc.otm.capability.energy import net.minecraft.util.RandomSource import ru.dbotthepony.mc.otm.capability.FlowDirection -import ru.dbotthepony.mc.otm.capability.iterateProviders import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.util.CapabilityListIterator import java.util.function.Supplier -import java.util.random.RandomGenerator open class CombinedEnergyStorage( final override val energyFlow: FlowDirection, - val provider: Supplier>, - val random: Supplier = Supplier { null } + provider: Supplier>, + random: Supplier = Supplier { null } ) : IMatteryEnergyStorage { + val iterator = CapabilityListIterator(provider, random) + + val provider get() = iterator.provider + val random get() = iterator.random + final override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::extractEnergy) + return iterator.iterate(howMuch, simulate, IMatteryEnergyStorage::extractEnergy) } final override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::receiveEnergy) + return iterator.iterate(howMuch, simulate, IMatteryEnergyStorage::receiveEnergy) } /* final override fun extractEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::extractEnergyChecked) + return iterator.iterate(howMuch, simulate, IMatteryEnergyStorage::extractEnergyChecked) } final override fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::receiveEnergyChecked) + return iterator.iterate(howMuch, simulate, IMatteryEnergyStorage::receiveEnergyChecked) } */ diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedProfiledEnergyStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedProfiledEnergyStorage.kt index 441f918c3..34c4e4204 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedProfiledEnergyStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/CombinedProfiledEnergyStorage.kt @@ -14,15 +14,15 @@ import java.util.random.RandomGenerator class CombinedProfiledEnergyStorage( energyFlow: FlowDirection, - provider: Supplier>>, + provider: Supplier>>, random: Supplier = Supplier { null } ) : CombinedEnergyStorage(energyFlow, provider, random), IProfiledMatteryEnergyStorage, IProfiledStorage.Combined, ITickable { - override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledEnergyStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) - override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledEnergyStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) + override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledEnergyStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) + override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledEnergyStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) override val receivedThisTick: Decimal - get() = provider.get().map { (it as ProfiledEnergyStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { (it as ProfiledEnergyStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus) override val transferredThisTick: Decimal - get() = provider.get().map { (it as ProfiledEnergyStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { (it as ProfiledEnergyStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus) override fun tick() { received.tick() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedMatterStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedMatterStorage.kt index d18c5b298..433a36e46 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedMatterStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedMatterStorage.kt @@ -2,32 +2,36 @@ package ru.dbotthepony.mc.otm.capability.matter import net.minecraft.util.RandomSource import ru.dbotthepony.mc.otm.capability.FlowDirection -import ru.dbotthepony.mc.otm.capability.iterateProviders import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.util.CapabilityListIterator import java.util.function.Supplier -import java.util.random.RandomGenerator open class CombinedMatterStorage( final override val matterFlow: FlowDirection, - val provider: Supplier>, - val random: Supplier = Supplier { null } + provider: Supplier>, + random: Supplier = Supplier { null } ) : IMatterStorage { + val iterator = CapabilityListIterator(provider, random) + + val provider get() = iterator.provider + val random get() = iterator.random + final override var storedMatter: Decimal - get() = provider.get().map { it.storedMatter }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { it.storedMatter }.reduce(Decimal.ZERO, Decimal::plus) set(value) { throw UnsupportedOperationException() } final override val maxStoredMatter: Decimal - get() = provider.get().map { it.maxStoredMatter }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { it.maxStoredMatter }.reduce(Decimal.ZERO, Decimal::plus) final override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::receiveMatter) + return iterator.iterate(howMuch, simulate, IMatterStorage::receiveMatter) } final override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal { - return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::extractMatter) + return iterator.iterate(howMuch, simulate, IMatterStorage::extractMatter) } final override val canSetMatterLevel: Boolean diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedProfiledMatterStorage.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedProfiledMatterStorage.kt index 34576bf89..b436d9fc5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedProfiledMatterStorage.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/matter/CombinedProfiledMatterStorage.kt @@ -14,15 +14,15 @@ import java.util.random.RandomGenerator class CombinedProfiledMatterStorage( matterFlow: FlowDirection, - provider: Supplier>>, + provider: Supplier>>, random: Supplier = Supplier { null } ) : CombinedMatterStorage(matterFlow, provider, random), IProfiledMatterStorage, IProfiledStorage.Combined, ITickable { - override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledMatterStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) - override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledMatterStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) + override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledMatterStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) + override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledMatterStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE) override val receivedThisTick: Decimal - get() = provider.get().map { (it as ProfiledMatterStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { (it as ProfiledMatterStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus) override val transferredThisTick: Decimal - get() = provider.get().map { (it as ProfiledMatterStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus) + get() = provider.get().iterator().map { (it as ProfiledMatterStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus) override fun tick() { received.tick() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/IMultiblockAccess.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/IMultiblockAccess.kt index 420c7fa32..5f0bcdd9b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/IMultiblockAccess.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/IMultiblockAccess.kt @@ -3,13 +3,10 @@ package ru.dbotthepony.mc.otm.core.multiblock import it.unimi.dsi.fastutil.objects.Reference2IntMap import net.minecraft.core.BlockPos import net.minecraft.core.Direction -import net.minecraft.world.level.Level import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.AABB -import ru.dbotthepony.mc.otm.core.collect.WeakHashSet -import java.util.* +import ru.dbotthepony.kommons.util.Listenable interface IMultiblockAccess { /** @@ -19,6 +16,19 @@ interface IMultiblockAccess { val currentDirection: Direction? val currentNodes: Map + /** + * Arbitrary number representing counter which increases when *something* has changed regarding multiblock configuraiton, + * probably something not even visible in public interface + * + * Useful for caching data structures which derive from current state of multiblock + */ + val generation: Int + + /** + * [listener] will be executed whenever [generation] changes + */ + fun onGenerationChanges(listener: Runnable): Listenable.L + val boundingBox: AABB? /** diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/ShapedMultiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/ShapedMultiblock.kt index 0316b47d9..abd67ade9 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/ShapedMultiblock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/multiblock/ShapedMultiblock.kt @@ -2,8 +2,6 @@ package ru.dbotthepony.mc.otm.core.multiblock import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap -import it.unimi.dsi.fastutil.ints.IntArrayList -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2IntMap import it.unimi.dsi.fastutil.objects.Reference2IntMaps import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap @@ -16,8 +14,7 @@ import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.AABB -import ru.dbotthepony.mc.otm.core.AABB -import ru.dbotthepony.mc.otm.core.addAll +import ru.dbotthepony.kommons.util.Listenable import ru.dbotthepony.mc.otm.core.collect.collect import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.getBlockEntityNow @@ -30,7 +27,6 @@ import ru.dbotthepony.mc.otm.network.syncher.ISynchable import java.io.Closeable import java.util.LinkedList import java.util.concurrent.CopyOnWriteArrayList -import java.util.function.BooleanSupplier import kotlin.collections.ArrayDeque import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -49,6 +45,25 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti private val east = Config(Direction.EAST, pos, factory.east) private val configurations = ImmutableList.of(north, south, west, east) + override var generation: Int = 0 + private set + + private val changeListeners = CopyOnWriteArrayList() + + private inner class ChangeListener(val runnable: Runnable) : Listenable.L { + init { + changeListeners.add(this) + } + + override fun remove() { + changeListeners.remove(this) + } + } + + override fun onGenerationChanges(listener: Runnable): Listenable.L { + return ChangeListener(listener) + } + override val hasRemotes: Boolean get() = remotes.isNotEmpty() @@ -147,6 +162,8 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti val be2 = blockEntity if (be1 != be2) { + generation++ + if (be1 != null) { if (be1 is IMultiblockListener) referencedListeners.dereference(be1) @@ -170,6 +187,8 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti val old = this.blockState if (state != old) { + generation++ + if (old != null) { assignedBlockStateLists.forEach { it[old] = it.getInt(old) - 1 @@ -216,6 +235,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } private fun clearFull() { + generation++ val blockEntity = blockEntity if (blockEntity != null) { @@ -251,6 +271,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } fun clear() { + generation++ val blockEntity = blockEntity if (blockEntity != null) { @@ -269,6 +290,14 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti private val referencedListeners = MultiblockListenerSet(this) + override var generation: Int + get() = this@ShapedMultiblock.generation + private set(value) { this@ShapedMultiblock.generation = value } + + override fun onGenerationChanges(listener: Runnable): Listenable.L { + return this@ShapedMultiblock.onGenerationChanges(listener) + } + val index = when (currentDirection) { Direction.NORTH -> 0 Direction.SOUTH -> 1 @@ -349,6 +378,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } fun clear() { + generation++ referencedListeners.clear() tag2BlockEntity.values.forEach { it.clear() } tag2BlockState.values.forEach { it.clear() } @@ -366,7 +396,12 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } override var isValid: Boolean = false - private set + private set(value) { + if (value != field) { + field = value + generation++ + } + } private var iterator = this.parts.values.iterator() private var validParts = 0 @@ -565,6 +600,12 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } private var activeConfig: Config = north + set(value) { + if (field !== value) { + field = value + generation++ + } + } override val currentDirection: Direction? get() = if (isValid) activeConfig.currentDirection else null @@ -606,6 +647,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti fun update(levelAccessor: LevelAccessor): Boolean { isUpdating = true + val thisGeneration = generation try { val configurations = LinkedList() @@ -627,6 +669,11 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti activeConfig = config isValid = true remotes.forEach { it.listener.run() } + + if (thisGeneration != generation) { + changeListeners.forEach { it.runnable.run() } + } + return true } else { config.clear() @@ -634,6 +681,10 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } } + if (thisGeneration != generation) { + changeListeners.forEach { it.runnable.run() } + } + return false } finally { isUpdating = false @@ -642,6 +693,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean { isUpdating = true + val thisGeneration = generation try { var changes = false @@ -673,6 +725,10 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti remotes.forEach { it.listener.run() } } + if (thisGeneration != generation) { + changeListeners.forEach { it.runnable.run() } + } + return isValid } finally { isUpdating = false @@ -680,6 +736,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti } override fun close() { + changeListeners.clear() remotes.forEach { it.close() } configurations.forEach { it.clear() } isValid = false diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CapabilityListIterator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CapabilityListIterator.kt new file mode 100644 index 000000000..086c911b7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CapabilityListIterator.kt @@ -0,0 +1,53 @@ +package ru.dbotthepony.mc.otm.core.util + +import it.unimi.dsi.fastutil.objects.ObjectArrayList +import net.minecraft.util.RandomSource +import ru.dbotthepony.mc.otm.core.math.Decimal +import ru.dbotthepony.mc.otm.core.shuffle +import java.util.function.Supplier + +class CapabilityListIterator( + val provider: Supplier>, + val random: Supplier, +) { + private var shuffleCache = IntArray(0) + + private fun doIterate(values: List, value: Decimal, simulate: Boolean, walker: T.(Decimal, Boolean) -> Decimal, indexProvider: () -> IntIterator): Decimal { + var leftover = value + + for (index in indexProvider()) { + val provider = values[index] + leftover -= walker(provider, leftover, true) + if (leftover.signum() <= 0) break + } + + if (simulate) return value - leftover + + leftover = value + + for (index in indexProvider()) { + val provider = values[index] + leftover -= walker(provider, leftover, false) + if (leftover.signum() <= 0) break + } + + return value - leftover + } + + fun iterate(value: Decimal, simulate: Boolean, walker: T.(Decimal, Boolean) -> Decimal): Decimal { + val providers = provider.get() + if (providers.isEmpty()) return Decimal.ZERO + val random = random.get() + + if (random == null || providers.size == 1) { + return doIterate(providers, value, simulate, walker, providers.indices::iterator) + } else { + if (shuffleCache.size != providers.size) { + shuffleCache = IntArray(providers.size) { it } + } + + shuffleCache.shuffle(random) + return doIterate(providers, value, simulate, walker, shuffleCache::iterator) + } + } +}