The fastest code is code which doesn't execute

Side automation no longer ticks if it doesn't automate
This commit is contained in:
DBotThePony 2023-08-03 18:32:41 +07:00
parent c08e262638
commit 20d478cbfb
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 178 additions and 156 deletions

View File

@ -180,22 +180,6 @@ fun tickServer(ticker: IConditionalTickable) {
postServerTick.add(ticker, SERVER_IS_LIVE, "Server is stopping") postServerTick.add(ticker, SERVER_IS_LIVE, "Server is stopping")
} }
fun tickUntilServerPre(ticker: () -> Boolean) {
preServerTick.until(ticker, SERVER_IS_LIVE, "Server is stopping")
}
fun tickUntilServer(ticker: () -> Boolean) {
postServerTick.until(ticker, SERVER_IS_LIVE, "Server is stopping")
}
fun tickWhileServerPre(condition: () -> Boolean, ticker: () -> Unit) {
preServerTick.`while`(condition, ticker, SERVER_IS_LIVE, "Server is stopping")
}
fun tickWhileServer(condition: () -> Boolean, ticker: () -> Unit) {
postServerTick.`while`(condition, ticker, SERVER_IS_LIVE, "Server is stopping")
}
fun Level.once(ticker: ITickable) { fun Level.once(ticker: ITickable) {
if (this.isClientSide) return if (this.isClientSide) return

View File

@ -129,18 +129,21 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
} }
inner class Piece(val side: RelativeSide) : IFluidHandler, ITickable { inner class Piece(val side: RelativeSide) : IFluidHandler, ITickable {
init { private val ticker = tickList.Ticker(this)
tickList.always(this) private val controller = sides[side]!!.Cap(ForgeCapabilities.FLUID_HANDLER, this)
private val neighbour by sides[side]!!.track(ForgeCapabilities.FLUID_HANDLER)
private fun updateTickerState() {
ticker.isEnabled = (automatePull || automatePush) && flow != FlowDirection.NONE && !redstoneControl.isBlockedByRedstone
}
init {
// https://tenor.com/view/simp-metal-gear-liquid-snake-running-gif-16717852 // https://tenor.com/view/simp-metal-gear-liquid-snake-running-gif-16717852
savetables.enum(::flow, "fluid_${side}_flow", FlowDirection::valueOf) savetables.enum(::flow, "fluid_${side}_flow", FlowDirection::valueOf)
savetables.bool(::automatePull, "fluid_${side}_pull") savetables.bool(::automatePull, "fluid_${side}_pull")
savetables.bool(::automatePush, "fluid_${side}_push") savetables.bool(::automatePush, "fluid_${side}_push")
} }
private val controller = sides[side]!!.Cap(ForgeCapabilities.FLUID_HANDLER, this)
private val neighbour by sides[side]!!.track(ForgeCapabilities.FLUID_HANDLER)
var flow by synchronizer.enum(possibleModes, setter = { value, access, setByRemote -> var flow by synchronizer.enum(possibleModes, setter = { value, access, setByRemote ->
require(possibleModes.isSupertype(value)) { "Energy mode $value is not allowed (allowed modes: ${possibleModes.family})" } require(possibleModes.isSupertype(value)) { "Energy mode $value is not allowed (allowed modes: ${possibleModes.family})" }
@ -154,6 +157,8 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
controller.close() controller.close()
controller.expose() controller.expose()
} }
updateTickerState()
} }
}) })
@ -162,12 +167,25 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
set(value) { set(value) {
field = value field = value
setChangedLight() setChangedLight()
updateTickerState()
} }
// var automatePush by synchronizer.bool().property // var automatePush by synchronizer.bool().property
var automatePush = false var automatePush = false
set(value) { set(value) {
field = value field = value
setChangedLight() setChangedLight()
updateTickerState()
}
init {
tickList.once {
redstoneControl.addListener {
updateTickerState()
}
updateTickerState()
}
} }
override fun tick() { override fun tick() {
@ -324,25 +342,40 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
override val canSetBatteryLevel: Boolean by energy::canSetBatteryLevel override val canSetBatteryLevel: Boolean by energy::canSetBatteryLevel
private val ticker = tickList.Ticker(this)
private fun updateTickerState() {
ticker.isEnabled = (automatePull || automatePush) && energyFlow != FlowDirection.NONE && !redstoneControl.isBlockedByRedstone
}
// var automatePull by synchronizer.bool().property // var automatePull by synchronizer.bool().property
var automatePull = false var automatePull = false
set(value) { set(value) {
field = value field = value
setChangedLight() setChangedLight()
updateTickerState()
} }
// var automatePush by synchronizer.bool().property // var automatePush by synchronizer.bool().property
var automatePush = false var automatePush = false
set(value) { set(value) {
field = value field = value
setChangedLight() setChangedLight()
updateTickerState()
} }
init { init {
tickList.always(this)
savetables.enum(::energyFlow, "energy_${side}_flow", FlowDirection::valueOf) savetables.enum(::energyFlow, "energy_${side}_flow", FlowDirection::valueOf)
savetables.bool(::automatePull, "energy_${side}_pull") savetables.bool(::automatePull, "energy_${side}_pull")
savetables.bool(::automatePush, "energy_${side}_push") savetables.bool(::automatePush, "energy_${side}_push")
tickList.once {
redstoneControl.addListener {
updateTickerState()
}
updateTickerState()
}
} }
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal { override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
@ -406,6 +439,8 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
controller.expose() controller.expose()
} }
} }
updateTickerState()
} }
}) })
@ -533,11 +568,26 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
inner class Piece(val side: RelativeSide) : IItemHandler, ITickable { inner class Piece(val side: RelativeSide) : IItemHandler, ITickable {
private var currentHandler: IItemHandler = EmptyItemHandler private var currentHandler: IItemHandler = EmptyItemHandler
set(value) {
field = value
updateTickerState()
}
private val capController = sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this) private val capController = sides[side]!!.Cap(ForgeCapabilities.ITEM_HANDLER, this)
private val neighbour by sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER) private val neighbour by sides[side]!!.track(ForgeCapabilities.ITEM_HANDLER)
private val ticker = tickList.Ticker(this)
private var innerSlotPull = 0
private var outerSlotPull = 0
private var innerSlotPush = 0
private var outerSlotPush = 0
private fun updateTickerState() {
ticker.isEnabled = (automatePull || automatePush) && mode != ItemHandlerMode.DISABLED && !redstoneControl.isBlockedByRedstone && currentHandler.slots != 0
}
init { init {
tickList.always(this)
capController.close() capController.close()
} }
@ -565,28 +615,6 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
} }
}) })
/*var automatePull by synchronizer.bool(setter = { value, access, _ ->
if (access.readBoolean() != value) {
access.write(value)
if (value) {
innerSlotPush = 0
outerSlotPush = 0
}
}
}).property
var automatePush by synchronizer.bool(setter = { value, access, _ ->
if (access.readBoolean() != value) {
access.write(value)
if (value) {
innerSlotPush = 0
outerSlotPush = 0
}
}
}).property*/
var automatePull = false var automatePull = false
set(value) { set(value) {
if (field != value) { if (field != value) {
@ -597,6 +625,8 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
innerSlotPush = 0 innerSlotPush = 0
outerSlotPush = 0 outerSlotPush = 0
} }
updateTickerState()
} }
} }
@ -610,6 +640,8 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
innerSlotPush = 0 innerSlotPush = 0
outerSlotPush = 0 outerSlotPush = 0
} }
updateTickerState()
} }
} }
@ -617,13 +649,15 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
savetables.bool(::automatePull, "itemhandler_${side}_automatePull") savetables.bool(::automatePull, "itemhandler_${side}_automatePull")
savetables.bool(::automatePush, "itemhandler_${side}_automatePush") savetables.bool(::automatePush, "itemhandler_${side}_automatePush")
savetables.enum(::mode, "itemhandler_${side}_mode", ItemHandlerMode::valueOf) savetables.enum(::mode, "itemhandler_${side}_mode", ItemHandlerMode::valueOf)
tickList.once {
redstoneControl.addListener {
updateTickerState()
} }
private var innerSlotPull = 0 updateTickerState()
private var outerSlotPull = 0 }
}
private var innerSlotPush = 0
private var outerSlotPush = 0
override fun tick() { override fun tick() {
if (mode == ItemHandlerMode.DISABLED || !automatePull && !automatePush || redstoneControl.isBlockedByRedstone || currentHandler.slots == 0) if (mode == ItemHandlerMode.DISABLED || !automatePull && !automatePush || redstoneControl.isBlockedByRedstone || currentHandler.slots == 0)

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import it.unimi.dsi.fastutil.booleans.BooleanConsumer
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.mapString import ru.dbotthepony.mc.otm.core.nbt.mapString
@ -13,9 +14,14 @@ interface IRedstoneControlled {
abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> { abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
abstract var redstoneSetting: RedstoneSetting abstract var redstoneSetting: RedstoneSetting
abstract var redstoneSignal: Int abstract var redstoneSignal: Int
protected val listeners = ArrayList<BooleanConsumer>()
val isBlockedByRedstone: Boolean get() = !redstoneSetting.test(redstoneSignal) val isBlockedByRedstone: Boolean get() = !redstoneSetting.test(redstoneSignal)
fun addListener(callback: BooleanConsumer) {
listeners.add(callback)
}
override fun serializeNBT(): CompoundTag { override fun serializeNBT(): CompoundTag {
return CompoundTag().also { return CompoundTag().also {
it[SETTING_KEY] = redstoneSetting.toString() it[SETTING_KEY] = redstoneSetting.toString()
@ -47,7 +53,11 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
val old = isBlockedByRedstone val old = isBlockedByRedstone
field = level field = level
val state = isBlockedByRedstone val state = isBlockedByRedstone
if (state != old) {
valueChanges.invoke(state, old) valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
}
} }
override var redstoneSignal: Int = 0 override var redstoneSignal: Int = 0
@ -56,7 +66,11 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
val old = isBlockedByRedstone val old = isBlockedByRedstone
field = setting field = setting
val state = isBlockedByRedstone val state = isBlockedByRedstone
if (state != old) {
valueChanges.invoke(state, old) valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
}
} }
} }
@ -72,7 +86,11 @@ class SynchronizedRedstoneControl(
val old = isBlockedByRedstone val old = isBlockedByRedstone
access.write(value) access.write(value)
val state = isBlockedByRedstone val state = isBlockedByRedstone
if (state != old) {
valueChanges.invoke(state, old) valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
}
} }
}) })
@ -84,7 +102,11 @@ class SynchronizedRedstoneControl(
val old = isBlockedByRedstone val old = isBlockedByRedstone
access.write(value) access.write(value)
val state = isBlockedByRedstone val state = isBlockedByRedstone
if (state != old) {
valueChanges.invoke(state, old) valueChanges.invoke(state, old)
listeners.forEach { it.accept(state) }
}
} }
}).property }).property
} }

View File

@ -10,6 +10,7 @@ import com.google.common.collect.ImmutableSet
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive import com.google.gson.JsonPrimitive
import it.unimi.dsi.fastutil.objects.ObjectComparators
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtAccounter
@ -417,3 +418,12 @@ fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, from
fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) { fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) {
add(searchInsertionIndex(element, comparator), element) add(searchInsertionIndex(element, comparator), element)
} }
/**
* Inserts [element] into [MutableList] at index determined by comparing values themselves
*
* If [MutableList] is not sorted, result of this function is undefined
*/
fun <E : Comparable<E>> MutableList<E>.addSorted(element: E) {
add(searchInsertionIndex(element, ObjectComparators.NATURAL_COMPARATOR), element)
}

View File

@ -1,16 +1,18 @@
package ru.dbotthepony.mc.otm.core.util package ru.dbotthepony.mc.otm.core.util
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.core.addSorted
class TickList : ITickable { class TickList : ITickable {
private val conditional = ArrayDeque<IConditionalTickable>() private val conditional = ArrayDeque<IConditionalTickable>()
private val conditionalValveTime = ArrayList<IConditionalTickable>() private val conditionalQueued = ArrayList<IConditionalTickable>()
private val once = ArrayDeque<ITickable>() private val once = ArrayDeque<ITickable>()
private val onceValveTime = ArrayList<ITickable>() private val onceQueued = ArrayList<ITickable>()
private val always = ArrayList<ITickable>() private val always = ArrayList<ITickable>()
private val alwaysValveTime = ArrayList<ITickable>() private val alwaysQueued = ArrayList<ITickable>()
private val toRemoveFromAlways = ArrayList<ITickable>()
private val timers = ArrayDeque<Timer>() private val timers = ArrayDeque<Timer>()
@ -19,45 +21,18 @@ class TickList : ITickable {
var ticks = 0 var ticks = 0
private set private set
private var nothingToDo = true inner class Timer(val timerTicks: Int, val runnable: Runnable) : Comparable<Timer> {
inner class Timer(val timerTicks: Int, val runnable: Runnable) {
val ringAt = ticks + timerTicks val ringAt = ticks + timerTicks
var finished = false var finished = false
private set private set
init { init {
nothingToDo = false timers.addSorted(this)
if (timers.isEmpty()) {
timers.addLast(this)
} else {
val iterator = timers.listIterator()
var hit = false
for (value in iterator) {
if (value.ringAt == ringAt) {
hit = true
iterator.add(this)
break
} else if (value.ringAt > ringAt) {
if (iterator.hasPrevious()) {
iterator.previous()
iterator.add(this)
} else {
timers.addFirst(this)
} }
hit = true override fun compareTo(other: Timer): Int {
break return ringAt.compareTo(other.ringAt)
}
}
if (!hit) {
timers.addLast(this)
}
}
} }
fun execute() { fun execute() {
@ -67,13 +42,49 @@ class TickList : ITickable {
} }
} }
fun add(ticker: IConditionalTickable) { inner class Ticker(parent: ITickable) : ITickable by parent {
if (inTicker) { init {
conditionalValveTime.add(ticker) add(this, always, alwaysQueued)
} else {
conditional.addFirst(ticker)
nothingToDo = false
} }
var isEnabled = true
set(value) {
if (field != value) {
field = value
if (value) {
add(this, always, alwaysQueued)
} else {
alwaysQueued.remove(this)
if (inTicker) {
toRemoveFromAlways.add(this)
} else {
always.remove(this)
}
}
}
}
fun disable() {
isEnabled = false
}
fun enable() {
isEnabled = true
}
}
private fun <T : Any> add(value: T, regular: MutableList<T>, queue: MutableList<T>) {
if (inTicker) {
queue.add(value)
} else {
regular.add(value)
}
}
fun add(ticker: IConditionalTickable) {
add(ticker, conditional, conditionalQueued)
} }
fun add(ticker: IConditionalTickable, condition: Boolean, reason: String) { fun add(ticker: IConditionalTickable, condition: Boolean, reason: String) {
@ -86,12 +97,7 @@ class TickList : ITickable {
} }
fun once(ticker: ITickable) { fun once(ticker: ITickable) {
if (inTicker) { add(ticker, once, onceQueued)
onceValveTime.add(ticker)
} else {
once.addFirst(ticker)
nothingToDo = false
}
} }
fun once(ticker: ITickable, condition: Boolean, reason: String) { fun once(ticker: ITickable, condition: Boolean, reason: String) {
@ -104,12 +110,7 @@ class TickList : ITickable {
} }
fun always(ticker: ITickable) { fun always(ticker: ITickable) {
if (inTicker) { add(ticker, always, alwaysQueued)
alwaysValveTime.add(ticker)
} else {
always.add(ticker)
nothingToDo = false
}
} }
fun timer(timerTicks: Int, action: Runnable, condition: Boolean, reason: String): Timer? { fun timer(timerTicks: Int, action: Runnable, condition: Boolean, reason: String): Timer? {
@ -125,23 +126,7 @@ class TickList : ITickable {
return Timer(timerTicks, action) return Timer(timerTicks, action)
} }
fun until(ticker: () -> Boolean) = add(IConditionalTickable.wrap(ticker))
fun `while`(tickerCondition: () -> Boolean, ticker: () -> Unit) = add(
IConditionalTickable.wrap(
tickerCondition,
ticker
)
)
fun until(ticker: () -> Boolean, condition: Boolean, reason: String) = add(IConditionalTickable.wrap(ticker), condition, reason)
fun `while`(tickerCondition: () -> Boolean, ticker: () -> Unit, condition: Boolean, reason: String) = add(
IConditionalTickable.wrap(tickerCondition, ticker), condition, reason)
override fun tick() { override fun tick() {
if (nothingToDo) {
return
}
if (inTicker) { if (inTicker) {
throw ConcurrentModificationException("Already ticking") throw ConcurrentModificationException("Already ticking")
} }
@ -150,23 +135,12 @@ class TickList : ITickable {
inTicker = true inTicker = true
try { try {
var nothingToDo = true
val conditional = conditional
val once = once
val always = always
val alwaysValveTime = alwaysValveTime
val conditionalValveTime = conditionalValveTime
val onceValveTime = onceValveTime
val timers = timers
if (conditional.isNotEmpty()) { if (conditional.isNotEmpty()) {
val iterator = conditional.iterator() val iterator = conditional.iterator()
for (ticker in iterator) { for (ticker in iterator) {
if (!ticker.tick()) { if (!ticker.tick()) {
iterator.remove() iterator.remove()
} else {
nothingToDo = false
} }
} }
} }
@ -179,40 +153,40 @@ class TickList : ITickable {
once.clear() once.clear()
} }
if (toRemoveFromAlways.isNotEmpty()) {
for (v in toRemoveFromAlways) always.remove(v)
toRemoveFromAlways.clear()
}
if (always.isNotEmpty()) { if (always.isNotEmpty()) {
for (ticker in always) { for (ticker in always) {
ticker.tick() ticker.tick()
} }
nothingToDo = false
} }
if (alwaysValveTime.isNotEmpty()) { if (alwaysQueued.isNotEmpty()) {
always.addAll(alwaysValveTime) always.ensureCapacity(always.size + alwaysQueued.size)
alwaysValveTime.clear() for (v in alwaysQueued) always.add(v) // avoid toArray()
nothingToDo = false alwaysQueued.clear()
} }
if (conditionalValveTime.isNotEmpty()) { if (conditionalQueued.isNotEmpty()) {
for (ticker in conditionalValveTime) { for (ticker in conditionalQueued) {
conditional.addFirst(ticker) conditional.addFirst(ticker)
} }
conditionalValveTime.clear() conditionalQueued.clear()
nothingToDo = false
} }
if (onceValveTime.isNotEmpty()) { if (onceQueued.isNotEmpty()) {
for (ticker in onceValveTime) { for (ticker in onceQueued) {
once.addFirst(ticker) once.addFirst(ticker)
} }
onceValveTime.clear() onceQueued.clear()
nothingToDo = false
} }
while (timers.isNotEmpty()) { while (timers.isNotEmpty()) {
nothingToDo = false
val head = timers.first() val head = timers.first()
if (head.ringAt <= ticks) { if (head.ringAt <= ticks) {
@ -222,8 +196,6 @@ class TickList : ITickable {
break break
} }
} }
this.nothingToDo = nothingToDo
} finally { } finally {
inTicker = false inTicker = false
} }
@ -233,13 +205,13 @@ class TickList : ITickable {
if (inTicker) throw ConcurrentModificationException() if (inTicker) throw ConcurrentModificationException()
conditional.clear() conditional.clear()
conditionalValveTime.clear() conditionalQueued.clear()
once.clear() once.clear()
onceValveTime.clear() onceQueued.clear()
always.clear() always.clear()
alwaysValveTime.clear() alwaysQueued.clear()
timers.clear() timers.clear()
} }