Compare commits

...

2 Commits

10 changed files with 205 additions and 93 deletions

View File

@ -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.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ObjectArraySet import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.ObjectIterators
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory 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.inventory.AbstractContainerMenu
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.kommons.collect.toList
import ru.dbotthepony.kommons.util.DelegateSetter import ru.dbotthepony.kommons.util.DelegateSetter
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue 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.capability.matter.CombinedProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.ServerConfig 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.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.multiblock.ShapedMultiblock 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.math.times
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock 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.menu.tech.BlackHoleGeneratorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlockTags import ru.dbotthepony.mc.otm.registry.MBlockTags
@ -44,6 +44,7 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
MBlockEntities.BLACK_HOLE_GENERATOR, blockPos, blockState) { MBlockEntities.BLACK_HOLE_GENERATOR, blockPos, blockState) {
var multiblock: ShapedMultiblock? = null var multiblock: ShapedMultiblock? = null
private set private set
private var multiblockSyncher: Closeable? = null private var multiblockSyncher: Closeable? = null
var lastRange by syncher.int(-1, setter = DelegateSetter { field, value -> var lastRange by syncher.int(-1, setter = DelegateSetter { field, value ->
@ -51,13 +52,23 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
multiblockSyncher?.close() multiblockSyncher?.close()
multiblockSyncher = null multiblockSyncher = null
multiblock?.close() multiblock?.close()
energyTarget.invalidate()
matterTarget.invalidate()
multiblock = null multiblock = null
field.accept(value) field.accept(value)
} else if (value != field.value) { } else if (value != field.value) {
multiblockSyncher?.close() multiblockSyncher?.close()
multiblock?.close() multiblock?.close()
multiblock = CONFIGURATIONS[value - 5].value.create(blockPos) multiblock = CONFIGURATIONS[value - 5].value.create(blockPos)
multiblock!!.onGenerationChanges(Runnable {
energyTarget.invalidate()
matterTarget.invalidate()
})
multiblockSyncher = syncher.add0(multiblock!!) multiblockSyncher = syncher.add0(multiblock!!)
energyTarget.invalidate()
matterTarget.invalidate()
field.accept(value) field.accept(value)
} }
}) })
@ -65,17 +76,16 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
var drawBuildingGuide by syncher.boolean() var drawBuildingGuide by syncher.boolean()
val energy = CombinedProfiledEnergyStorage( private val energyTarget = InvalidableLazy.Impl {
FlowDirection.NONE, multiblock?.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)?.iterator()?.map { it.energy }?.toList() ?: listOf()
{ multiblock?.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)?.iterator()?.map { it.energy } ?: ObjectIterators.emptyIterator() }, }
{ level?.random }
)
val matter = CombinedProfiledMatterStorage( private val matterTarget = InvalidableLazy.Impl {
FlowDirection.NONE, multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter }?.toList() ?: listOf()
{ multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter } ?: ObjectIterators.emptyIterator() }, }
{ level?.random }
) 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) { 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")), TARGET_MASS(TranslatableComponent("otm.gui.black_hole_generator.sustain.mode"), TranslatableComponent("otm.gui.black_hole_generator.sustain.desc")),
@ -159,7 +169,7 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
val multiblock = multiblock val multiblock = multiblock
if (multiblock != null) { if (multiblock != null) {
if (!multiblock.update(level, blockRotation.front)) { if (!multiblock.update(level, blockRotation.back)) {
this.lastRange = findBlackHoleRange() this.lastRange = findBlackHoleRange()
} else { } else {
val blackHole = multiblock.blockEntities(BLACK_HOLE).firstOrNull() ?: return val blackHole = multiblock.blockEntities(BLACK_HOLE).firstOrNull() ?: return

View File

@ -11,6 +11,8 @@ import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.energy.IEnergyStorage import net.neoforged.neoforge.energy.IEnergyStorage
import ru.dbotthepony.kommons.collect.flatMap import ru.dbotthepony.kommons.collect.flatMap
import ru.dbotthepony.kommons.collect.map 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.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage 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.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockListener import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockListener
import ru.dbotthepony.mc.otm.core.util.InvalidableLazy
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class EnergyInterfaceBlockEntity( class EnergyInterfaceBlockEntity(
@ -34,20 +37,22 @@ class EnergyInterfaceBlockEntity(
val energyInterfaceTarget: ProfiledEnergyStorage<*> val energyInterfaceTarget: ProfiledEnergyStorage<*>
} }
private val multiblocks = LinkedHashSet<IMultiblockAccess>() private val multiblocks = LinkedHashMap<IMultiblockAccess, Listenable.L>()
private val targets = InvalidableLazy.Impl {
multiblocks.keys.iterator().flatMap { it.blockEntities(TARGET).iterator() }.map { it.energyInterfaceTarget }.toList()
}
val energy = CombinedProfiledEnergyStorage( val energy = CombinedProfiledEnergyStorage(FlowDirection.input(isInput), targets::value, { level?.random })
FlowDirection.input(isInput),
{ multiblocks.iterator().flatMap { it.blockEntities(TARGET).iterator() }.map { it.energyInterfaceTarget } },
{ level?.random }
)
override fun onAddedToMultiblock(multiblock: IMultiblockAccess) { 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) { 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) { val container = object : MatteryContainer(::markDirtyFast, CAPACITY) {
@ -59,6 +64,11 @@ class EnergyInterfaceBlockEntity(
val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable) val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>() private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()
override fun setRemoved() {
super.setRemoved()
multiblocks.values.forEach { it.remove() }
}
init { init {
savetables.stateful(::container, BATTERY_KEY) savetables.stateful(::container, BATTERY_KEY)

View File

@ -1,10 +1,8 @@
package ru.dbotthepony.mc.otm.capability package ru.dbotthepony.mc.otm.capability
import com.google.common.collect.Streams import com.google.common.collect.Streams
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.util.RandomSource
import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack 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.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.core.util.formatFluidLevel import ru.dbotthepony.mc.otm.core.util.formatFluidLevel
import java.util.function.Supplier
import java.util.random.RandomGenerator
import java.util.stream.Stream import java.util.stream.Stream
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
@ -46,37 +41,6 @@ private val LOGGER = LogManager.getLogger()
val Player.matteryPlayer: MatteryPlayer get() = (this as IMatteryPlayer).otmPlayer val Player.matteryPlayer: MatteryPlayer get() = (this as IMatteryPlayer).otmPlayer
val LivingEntity.matteryPlayer: MatteryPlayer? get() = (this as? IMatteryPlayer)?.otmPlayer val LivingEntity.matteryPlayer: MatteryPlayer? get() = (this as? IMatteryPlayer)?.otmPlayer
fun <T> Supplier<out Iterator<T>>.iterateProviders(random: RandomSource?, value: Decimal, simulate: Boolean, walker: T.(Decimal, Boolean) -> Decimal): Decimal {
val providers = get()
val iteratorProvider: () -> Iterator<T>
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 * Does a checked energy receive, calls [IMatteryEnergyStorage.receiveEnergyChecked] if possible
*/ */

View File

@ -2,33 +2,37 @@ package ru.dbotthepony.mc.otm.capability.energy
import net.minecraft.util.RandomSource import net.minecraft.util.RandomSource
import ru.dbotthepony.mc.otm.capability.FlowDirection 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.map
import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.CapabilityListIterator
import java.util.function.Supplier import java.util.function.Supplier
import java.util.random.RandomGenerator
open class CombinedEnergyStorage( open class CombinedEnergyStorage(
final override val energyFlow: FlowDirection, final override val energyFlow: FlowDirection,
val provider: Supplier<out Iterator<IMatteryEnergyStorage>>, provider: Supplier<out List<IMatteryEnergyStorage>>,
val random: Supplier<RandomSource?> = Supplier { null } random: Supplier<RandomSource?> = Supplier { null }
) : IMatteryEnergyStorage { ) : 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 { 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 { 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 { 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 { 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)
} }
*/ */

View File

@ -14,15 +14,15 @@ import java.util.random.RandomGenerator
class CombinedProfiledEnergyStorage( class CombinedProfiledEnergyStorage(
energyFlow: FlowDirection, energyFlow: FlowDirection,
provider: Supplier<out Iterator<ProfiledEnergyStorage<*>>>, provider: Supplier<out List<ProfiledEnergyStorage<*>>>,
random: Supplier<RandomSource?> = Supplier { null } random: Supplier<RandomSource?> = Supplier { null }
) : CombinedEnergyStorage(energyFlow, provider, random), IProfiledMatteryEnergyStorage, IProfiledStorage.Combined, ITickable { ) : 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 received = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().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 transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledEnergyStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
override val receivedThisTick: Decimal 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 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() { override fun tick() {
received.tick() received.tick()

View File

@ -2,32 +2,36 @@ package ru.dbotthepony.mc.otm.capability.matter
import net.minecraft.util.RandomSource import net.minecraft.util.RandomSource
import ru.dbotthepony.mc.otm.capability.FlowDirection 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.map
import ru.dbotthepony.mc.otm.core.collect.reduce import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.CapabilityListIterator
import java.util.function.Supplier import java.util.function.Supplier
import java.util.random.RandomGenerator
open class CombinedMatterStorage( open class CombinedMatterStorage(
final override val matterFlow: FlowDirection, final override val matterFlow: FlowDirection,
val provider: Supplier<out Iterator<IMatterStorage>>, provider: Supplier<out List<IMatterStorage>>,
val random: Supplier<RandomSource?> = Supplier { null } random: Supplier<RandomSource?> = Supplier { null }
) : IMatterStorage { ) : IMatterStorage {
val iterator = CapabilityListIterator(provider, random)
val provider get() = iterator.provider
val random get() = iterator.random
final override var storedMatter: Decimal 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) { set(value) {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
final override val maxStoredMatter: Decimal 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 { 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 { 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 final override val canSetMatterLevel: Boolean

View File

@ -14,15 +14,15 @@ import java.util.random.RandomGenerator
class CombinedProfiledMatterStorage( class CombinedProfiledMatterStorage(
matterFlow: FlowDirection, matterFlow: FlowDirection,
provider: Supplier<out Iterator<ProfiledMatterStorage<*>>>, provider: Supplier<out List<ProfiledMatterStorage<*>>>,
random: Supplier<RandomSource?> = Supplier { null } random: Supplier<RandomSource?> = Supplier { null }
) : CombinedMatterStorage(matterFlow, provider, random), IProfiledMatterStorage, IProfiledStorage.Combined, ITickable { ) : 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 received = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().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 transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().iterator().map { (it as ProfiledMatterStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
override val receivedThisTick: Decimal 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 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() { override fun tick() {
received.tick() received.tick()

View File

@ -3,13 +3,10 @@ package ru.dbotthepony.mc.otm.core.multiblock
import it.unimi.dsi.fastutil.objects.Reference2IntMap import it.unimi.dsi.fastutil.objects.Reference2IntMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block 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.level.block.state.BlockState
import net.minecraft.world.phys.AABB import net.minecraft.world.phys.AABB
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet import ru.dbotthepony.kommons.util.Listenable
import java.util.*
interface IMultiblockAccess { interface IMultiblockAccess {
/** /**
@ -19,6 +16,19 @@ interface IMultiblockAccess {
val currentDirection: Direction? val currentDirection: Direction?
val currentNodes: Map<BlockPos, IMultiblockNode> val currentNodes: Map<BlockPos, IMultiblockNode>
/**
* 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? val boundingBox: AABB?
/** /**

View File

@ -2,8 +2,6 @@ package ru.dbotthepony.mc.otm.core.multiblock
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap 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.Reference2IntMap
import it.unimi.dsi.fastutil.objects.Reference2IntMaps import it.unimi.dsi.fastutil.objects.Reference2IntMaps
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap 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.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.AABB import net.minecraft.world.phys.AABB
import ru.dbotthepony.mc.otm.core.AABB import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.collect import ru.dbotthepony.mc.otm.core.collect.collect
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.getBlockEntityNow 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.io.Closeable
import java.util.LinkedList import java.util.LinkedList
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.BooleanSupplier
import kotlin.collections.ArrayDeque import kotlin.collections.ArrayDeque
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap 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 east = Config(Direction.EAST, pos, factory.east)
private val configurations = ImmutableList.of(north, south, west, east) private val configurations = ImmutableList.of(north, south, west, east)
override var generation: Int = 0
private set
private val changeListeners = CopyOnWriteArrayList<ChangeListener>()
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 override val hasRemotes: Boolean
get() = remotes.isNotEmpty() get() = remotes.isNotEmpty()
@ -147,6 +162,8 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
val be2 = blockEntity val be2 = blockEntity
if (be1 != be2) { if (be1 != be2) {
generation++
if (be1 != null) { if (be1 != null) {
if (be1 is IMultiblockListener) if (be1 is IMultiblockListener)
referencedListeners.dereference(be1) referencedListeners.dereference(be1)
@ -170,6 +187,8 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
val old = this.blockState val old = this.blockState
if (state != old) { if (state != old) {
generation++
if (old != null) { if (old != null) {
assignedBlockStateLists.forEach { assignedBlockStateLists.forEach {
it[old] = it.getInt(old) - 1 it[old] = it.getInt(old) - 1
@ -216,6 +235,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
private fun clearFull() { private fun clearFull() {
generation++
val blockEntity = blockEntity val blockEntity = blockEntity
if (blockEntity != null) { if (blockEntity != null) {
@ -251,6 +271,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
fun clear() { fun clear() {
generation++
val blockEntity = blockEntity val blockEntity = blockEntity
if (blockEntity != null) { if (blockEntity != null) {
@ -269,6 +290,14 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
private val referencedListeners = MultiblockListenerSet(this) 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) { val index = when (currentDirection) {
Direction.NORTH -> 0 Direction.NORTH -> 0
Direction.SOUTH -> 1 Direction.SOUTH -> 1
@ -349,6 +378,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
fun clear() { fun clear() {
generation++
referencedListeners.clear() referencedListeners.clear()
tag2BlockEntity.values.forEach { it.clear() } tag2BlockEntity.values.forEach { it.clear() }
tag2BlockState.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 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 iterator = this.parts.values.iterator()
private var validParts = 0 private var validParts = 0
@ -565,6 +600,12 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
private var activeConfig: Config = north private var activeConfig: Config = north
set(value) {
if (field !== value) {
field = value
generation++
}
}
override val currentDirection: Direction? override val currentDirection: Direction?
get() = if (isValid) activeConfig.currentDirection else null get() = if (isValid) activeConfig.currentDirection else null
@ -606,6 +647,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
fun update(levelAccessor: LevelAccessor): Boolean { fun update(levelAccessor: LevelAccessor): Boolean {
isUpdating = true isUpdating = true
val thisGeneration = generation
try { try {
val configurations = LinkedList<Config>() val configurations = LinkedList<Config>()
@ -627,6 +669,11 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
activeConfig = config activeConfig = config
isValid = true isValid = true
remotes.forEach { it.listener.run() } remotes.forEach { it.listener.run() }
if (thisGeneration != generation) {
changeListeners.forEach { it.runnable.run() }
}
return true return true
} else { } else {
config.clear() config.clear()
@ -634,6 +681,10 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
} }
if (thisGeneration != generation) {
changeListeners.forEach { it.runnable.run() }
}
return false return false
} finally { } finally {
isUpdating = false isUpdating = false
@ -642,6 +693,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean { fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean {
isUpdating = true isUpdating = true
val thisGeneration = generation
try { try {
var changes = false var changes = false
@ -673,6 +725,10 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
remotes.forEach { it.listener.run() } remotes.forEach { it.listener.run() }
} }
if (thisGeneration != generation) {
changeListeners.forEach { it.runnable.run() }
}
return isValid return isValid
} finally { } finally {
isUpdating = false isUpdating = false
@ -680,6 +736,7 @@ class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMulti
} }
override fun close() { override fun close() {
changeListeners.clear()
remotes.forEach { it.close() } remotes.forEach { it.close() }
configurations.forEach { it.clear() } configurations.forEach { it.clear() }
isValid = false isValid = false

View File

@ -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<T>(
val provider: Supplier<out List<T>>,
val random: Supplier<RandomSource?>,
) {
private var shuffleCache = IntArray(0)
private fun doIterate(values: List<T>, 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)
}
}
}