diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt index 4803fafc4..0130a93d4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/GlobalEventHandler.kt @@ -19,7 +19,6 @@ import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.core.util.IConditionalTickable import ru.dbotthepony.mc.otm.core.util.ITickable import ru.dbotthepony.mc.otm.core.util.TickList -import ru.dbotthepony.mc.otm.core.util.TimerQueue import java.util.* private val preServerTick = TickList() @@ -27,25 +26,22 @@ private val postServerTick = TickList() private val preWorldTick = WeakHashMap() private val postWorldTick = WeakHashMap() -private val preServerTickTimers = TimerQueue() -private val postServerTickTimers = TimerQueue() - -fun onceServerPre(inTicks: Int, callback: Runnable): TimerQueue.Timer? { +fun onceServerPre(inTicks: Int, callback: Runnable): TickList.Timer? { if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add timer $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping")) return null } - return preServerTickTimers.Timer(inTicks, callback) + return preServerTick.Timer(inTicks, callback) } -fun onceServer(inTicks: Int, callback: Runnable): TimerQueue.Timer? { +fun onceServer(inTicks: Int, callback: Runnable): TickList.Timer? { if (!SERVER_IS_LIVE) { LOGGER.error("Refusing to add ticker $callback in ticks $inTicks while server is dying", IllegalStateException("Server is stopping")) return null } - return postServerTickTimers.Timer(inTicks, callback) + return postServerTick.Timer(inTicks, callback) } private var _server: MinecraftServer? = null @@ -146,10 +142,8 @@ private val LOGGER = LogManager.getLogger() fun onServerTick(event: ServerTickEvent) { if (event.phase === TickEvent.Phase.START) { - preServerTickTimers.tick() preServerTick.tick() } else { - postServerTickTimers.tick() postServerTick.tick() } } @@ -259,8 +253,6 @@ fun Level.whilePre(condition: () -> Boolean, ticker: () -> Unit) { } private fun clear() { - preServerTickTimers.clear() - postServerTickTimers.clear() preServerTick.clear() postServerTick.clear() preWorldTick.clear() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt index 4f5473b31..0d812203e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt @@ -5,13 +5,11 @@ import net.minecraftforge.event.TickEvent import ru.dbotthepony.mc.otm.core.util.IConditionalTickable import ru.dbotthepony.mc.otm.core.util.ITickable import ru.dbotthepony.mc.otm.core.util.TickList -import ru.dbotthepony.mc.otm.core.util.TimerQueue import ru.dbotthepony.mc.otm.isClient private val preTickList = TickList() private val postTickList = TickList() -private val preTimerList = TimerQueue() -private val postTimerList = TimerQueue() + var LOGGED_IN = false private set @@ -27,12 +25,12 @@ fun onceClientPre(ticker: ITickable) { fun onceClient(inTicks: Int, ticker: Runnable) { check(isClient) { "Illegal side" } - postTimerList.add(inTicks, ticker, LOGGED_IN, "Not logged in") + postTickList.timer(inTicks, ticker, LOGGED_IN, "Not logged in") } fun onceClientPre(inTicks: Int, ticker: Runnable) { check(isClient) { "Illegal side" } - preTimerList.add(inTicks, ticker, LOGGED_IN, "Not logged in") + preTickList.timer(inTicks, ticker, LOGGED_IN, "Not logged in") } fun tickClient(ticker: IConditionalTickable) { @@ -63,10 +61,8 @@ fun tickWhileClientPre(condition: () -> Boolean, ticker: () -> Unit) { fun onClientTick(event: TickEvent.ClientTickEvent) { if (event.phase == TickEvent.Phase.START) { - preTimerList.tick() preTickList.tick() } else { - postTimerList.tick() postTickList.tick() } } @@ -74,17 +70,13 @@ fun onClientTick(event: TickEvent.ClientTickEvent) { fun onClientDisconnected(event: ClientPlayerNetworkEvent.LoggingOut) { LOGGED_IN = false - preTimerList.clear() preTickList.clear() - postTimerList.clear() postTickList.clear() } fun onClientConnected(event: ClientPlayerNetworkEvent.LoggingIn) { LOGGED_IN = true - preTimerList.clear() preTickList.clear() - postTimerList.clear() postTickList.clear() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TickList.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TickList.kt index f7404d92d..703e349e8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TickList.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TickList.kt @@ -2,16 +2,66 @@ package ru.dbotthepony.mc.otm.core.util import org.apache.logging.log4j.LogManager -class TickList { +class TickList : ITickable { private val conditional = ArrayDeque() + private val conditionalValveTime = ArrayList() + private val once = ArrayDeque() + private val onceValveTime = ArrayList() + private val always = ArrayList() private val alwaysValveTime = ArrayList() - private val conditionalValveTime = ArrayList() - private val onceValveTime = ArrayList() + private val timers = ArrayDeque() - private var inTicker = false + var inTicker = false + private set + var ticks = 0 + private set + + inner class Timer(val timerTicks: Int, val runnable: Runnable) { + val ringAt = ticks + timerTicks + + var finished = false + private set + + init { + 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 + break + } + } + + if (!hit) { + timers.addLast(this) + } + } + } + + fun execute() { + if (finished) return + runnable.run() + finished = true + } + } fun add(ticker: IConditionalTickable) { if (inTicker) { @@ -21,22 +71,6 @@ class TickList { } } - fun once(ticker: ITickable) { - if (inTicker) { - onceValveTime.add(ticker) - } else { - once.addFirst(ticker) - } - } - - fun always(ticker: ITickable) { - if (inTicker) { - alwaysValveTime.add(ticker) - } else { - always.add(ticker) - } - } - fun add(ticker: IConditionalTickable, condition: Boolean, reason: String) { if (!condition) { LOGGER.error("Refusing to add tickable $ticker because we $reason", IllegalStateException(reason)) @@ -46,6 +80,14 @@ class TickList { return add(ticker) } + fun once(ticker: ITickable) { + if (inTicker) { + onceValveTime.add(ticker) + } else { + once.addFirst(ticker) + } + } + fun once(ticker: ITickable, condition: Boolean, reason: String) { if (!condition) { LOGGER.error("Refusing to add tickable $ticker because we $reason", IllegalStateException(reason)) @@ -55,6 +97,27 @@ class TickList { return once(ticker) } + fun always(ticker: ITickable) { + if (inTicker) { + alwaysValveTime.add(ticker) + } else { + always.add(ticker) + } + } + + fun timer(timerTicks: Int, action: Runnable, condition: Boolean, reason: String): Timer? { + if (!condition) { + LOGGER.error("Refusing to add timer $action in $timerTicks ticks because we $reason", IllegalStateException(reason)) + return null + } + + return Timer(timerTicks, action) + } + + fun timer(timerTicks: Int, action: Runnable): Timer { + return Timer(timerTicks, action) + } + fun until(ticker: () -> Boolean) = add(IConditionalTickable.wrap(ticker)) fun `while`(tickerCondition: () -> Boolean, ticker: () -> Unit) = add( IConditionalTickable.wrap( @@ -67,66 +130,90 @@ class TickList { fun `while`(tickerCondition: () -> Boolean, ticker: () -> Unit, condition: Boolean, reason: String) = add( IConditionalTickable.wrap(tickerCondition, ticker), condition, reason) - fun tick() { + override fun tick() { if (inTicker) { throw ConcurrentModificationException("Already ticking") } + ticks++ inTicker = true - if (conditional.isNotEmpty()) { - val iterator = conditional.iterator() + try { + if (conditional.isNotEmpty()) { + val iterator = conditional.iterator() - for (ticker in iterator) { - if (!ticker.canTick) { - iterator.remove() - } else { + for (ticker in iterator) { + if (!ticker.canTick) { + iterator.remove() + } else { + ticker.tick() + } + } + } + + if (once.isNotEmpty()) { + for (ticker in once) { + ticker.tick() + } + + once.clear() + } + + if (always.isNotEmpty()) { + for (ticker in always) { ticker.tick() } } - } - if (once.isNotEmpty()) { - for (ticker in once) { - ticker.tick() + if (alwaysValveTime.isNotEmpty()) { + always.addAll(alwaysValveTime) + alwaysValveTime.clear() } - once.clear() - } + if (conditionalValveTime.isNotEmpty()) { + for (ticker in conditionalValveTime) { + conditional.addFirst(ticker) + } - if (always.isNotEmpty()) { - for (ticker in always) { - ticker.tick() - } - } - - if (alwaysValveTime.isNotEmpty()) { - always.addAll(alwaysValveTime) - alwaysValveTime.clear() - } - - if (conditionalValveTime.isNotEmpty()) { - for (ticker in conditionalValveTime) { - conditional.addFirst(ticker) + conditionalValveTime.clear() } - conditionalValveTime.clear() - } + if (onceValveTime.isNotEmpty()) { + for (ticker in onceValveTime) { + once.addFirst(ticker) + } - if (onceValveTime.isNotEmpty()) { - for (ticker in onceValveTime) { - once.addFirst(ticker) + onceValveTime.clear() } - onceValveTime.clear() - } + while (timers.isNotEmpty()) { + val head = timers.first() - inTicker = false + if (head.ringAt <= ticks) { + head.execute() + timers.removeFirst() + } else { + break + } + } + } finally { + inTicker = false + } } fun clear() { + if (inTicker) throw ConcurrentModificationException() + conditional.clear() + conditionalValveTime.clear() + once.clear() + onceValveTime.clear() + + always.clear() + alwaysValveTime.clear() + + timers.clear() } companion object { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TimerQueue.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TimerQueue.kt deleted file mode 100644 index 2ecbc87eb..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/TimerQueue.kt +++ /dev/null @@ -1,85 +0,0 @@ -package ru.dbotthepony.mc.otm.core.util - -import org.apache.logging.log4j.LogManager - -class TimerQueue { - private var ticks = 0 - private val list = ArrayDeque() - - inner class Timer(val timerTicks: Int, val runnable: Runnable) { - val ringAt = ticks + timerTicks - - var finished = false - private set - - init { - if (list.isEmpty()) { - list.addLast(this) - } else { - val iterator = list.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 { - list.addFirst(this) - } - - hit = true - break - } - } - - if (!hit) { - list.addLast(this) - } - } - } - - fun execute() { - if (finished) return - runnable.run() - finished = true - } - } - - fun add(timerTicks: Int, ticker: Runnable, condition: Boolean, reason: String): Timer? { - if (!condition) { - LOGGER.error("Refusing to add timer $ticker in $timerTicks ticks because we $reason", IllegalStateException(reason)) - return null - } - - return Timer(timerTicks, ticker) - } - - fun tick() { - ticks++ - - while (list.isNotEmpty()) { - val head = list.first() - - if (head.ringAt <= ticks) { - head.execute() - list.removeFirst() - } else { - break - } - } - } - - fun clear() { - ticks = 0 - list.clear() - } - - companion object { - private val LOGGER = LogManager.getLogger() - } -} diff --git a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/TimerQueueTests.kt b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/TimerQueueTests.kt index efd9f31be..d38e66af7 100644 --- a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/TimerQueueTests.kt +++ b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/TimerQueueTests.kt @@ -3,13 +3,13 @@ package ru.dbotthepony.mc.otm.tests import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import ru.dbotthepony.mc.otm.core.util.TimerQueue +import ru.dbotthepony.mc.otm.core.util.TickList object TimerQueueTests { @Test @DisplayName("TimerQueue test") fun test() { - val queue = TimerQueue() + val queue = TickList() var state = 0