Anton Tarasenko
4 years ago
6 changed files with 468 additions and 0 deletions
@ -0,0 +1,38 @@ |
|||||||
|
/** |
||||||
|
* Signal class implementation for `Timer`'s `OnElapsed` signal. |
||||||
|
* Copyright 2021 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class Timer_OnElapsed_Signal extends Signal; |
||||||
|
|
||||||
|
public final function Emit(Timer source) |
||||||
|
{ |
||||||
|
local Slot nextSlot; |
||||||
|
StartIterating(); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
while (nextSlot != none) |
||||||
|
{ |
||||||
|
Timer_OnElapsed_Slot(nextSlot).connect(source); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
} |
||||||
|
CleanEmptySlots(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
relatedSlotClass = class'Timer_OnElapsed_Slot' |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/** |
||||||
|
* Slot class implementation for `Timer`'s `OnElapsed` signal. |
||||||
|
* Copyright 2021 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class Timer_OnElapsed_Slot extends Slot; |
||||||
|
|
||||||
|
delegate connect(Timer source) |
||||||
|
{ |
||||||
|
DummyCall(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Constructor() |
||||||
|
{ |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
super.Finalizer(); |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/** |
||||||
|
* Class that acts like `Timer`, except it measures real, instead of in-game, |
||||||
|
* time. |
||||||
|
* Copyright 2021 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class RealTimer extends Timer; |
||||||
|
|
||||||
|
protected function float HandleTimeDilation( |
||||||
|
float timeDelta, |
||||||
|
float dilationCoefficient) |
||||||
|
{ |
||||||
|
return timeDelta / dilationCoefficient; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
/** |
||||||
|
* API that provides time-related methods. |
||||||
|
* Copyright 2021 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class TimeAPI extends AcediaObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates new `Timer`. Does not start it. |
||||||
|
* |
||||||
|
* @param interval Returned `Timer` will be configured to emit |
||||||
|
* `OnElapsed()` signals every `interval` seconds. |
||||||
|
* @param autoReset `true` will configure caller `Timer` to repeatedly emit |
||||||
|
* `OnElapsed()` every `interval` seconds, `false` (default value) will |
||||||
|
* make returned `Timer` emit that signal only once. |
||||||
|
* @return `Timer`, configured to emit `OnElapsed()` every `interval` seconds. |
||||||
|
* Not started. Guaranteed to be not `none`. |
||||||
|
*/ |
||||||
|
public final function Timer NewTimer( |
||||||
|
optional float interval, |
||||||
|
optional bool autoReset) |
||||||
|
{ |
||||||
|
return Timer(_.memory.Allocate(class'Timer')) |
||||||
|
.SetInterval(interval) |
||||||
|
.SetAutoReset(autoReset); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates and starts new `Timer`. |
||||||
|
* |
||||||
|
* @param interval Returned `Timer` will be configured to emit |
||||||
|
* `OnElapsed()` signals every `interval` seconds. |
||||||
|
* @param autoReset Setting this to `true` will configure caller `Timer` to |
||||||
|
* repeatedly emit `OnElapsed()` signal every `interval` seconds, `false` |
||||||
|
* (default value) will make returned `Timer` emit that signal only once. |
||||||
|
* @return `Timer`, configured to emit `OnElapsed()` every `interval` seconds. |
||||||
|
* Guaranteed to be not `none`. |
||||||
|
*/ |
||||||
|
public final function Timer StartTimer(float interval, optional bool autoReset) |
||||||
|
{ |
||||||
|
return Timer(_.memory.Allocate(class'Timer')) |
||||||
|
.SetInterval(interval) |
||||||
|
.SetAutoReset(autoReset) |
||||||
|
.Start(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates new `RealTimer`. Does not start it. |
||||||
|
* |
||||||
|
* @param interval Returned `RealTimer` will be configured to emit |
||||||
|
* `OnElapsed()` signals every `interval` seconds. |
||||||
|
* @param autoReset `true` will configure caller `RealTimer` to repeatedly |
||||||
|
* emit `OnElapsed()` every `interval` seconds, `false` (default value) |
||||||
|
* will make returned `RealTimer` emit that signal only once. |
||||||
|
* @return `RealTimer`, configured to emit `OnElapsed()` every `interval` |
||||||
|
* seconds. Not started. Guaranteed to be not `none`. |
||||||
|
*/ |
||||||
|
public final function RealTimer NewRealTimer( |
||||||
|
optional float interval, |
||||||
|
optional bool autoReset) |
||||||
|
{ |
||||||
|
local RealTimer newTimer; |
||||||
|
newTimer = RealTimer(_.memory.Allocate(class'RealTimer')); |
||||||
|
newTimer.SetInterval(interval).SetAutoReset(autoReset); |
||||||
|
return newTimer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates and starts new `RealTimer`. |
||||||
|
* |
||||||
|
* @param interval Returned `RealTimer` will be configured to emit |
||||||
|
* `OnElapsed()` signals every `interval` seconds. |
||||||
|
* @param autoReset Setting this to `true` will configure caller `RealTimer` |
||||||
|
* to repeatedly emit `OnElapsed()` signal every `interval` seconds, |
||||||
|
* `false` (default value) will make returned `RealTimer` emit that signal |
||||||
|
* only once. |
||||||
|
* @return `RealTimer`, configured to emit `OnElapsed()` every `interval` |
||||||
|
* seconds. Guaranteed to be not `none`. |
||||||
|
*/ |
||||||
|
public final function RealTimer StartRealTimer( |
||||||
|
float interval, |
||||||
|
optional bool autoReset) |
||||||
|
{ |
||||||
|
local RealTimer newTimer; |
||||||
|
newTimer = RealTimer(_.memory.Allocate(class'RealTimer')); |
||||||
|
newTimer.SetInterval(interval) |
||||||
|
.SetAutoReset(autoReset) |
||||||
|
.Start(); |
||||||
|
return newTimer; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,248 @@ |
|||||||
|
/** |
||||||
|
* Timer class that generates a signal after a set interval, with an option |
||||||
|
* to generate recurring signals. |
||||||
|
* Copyright 2021 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class Timer extends AcediaObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Because the `Timer` depends on the `Tick()` event, it has the same |
||||||
|
* resolution as a server's tick rate (frame rate for clients). This means that |
||||||
|
* the `OnElapsed()` signal will be emitted at an interval defined by the |
||||||
|
* `Tick()`. If the set interval between two signals is less than that, then |
||||||
|
* `Timer` might emit several signals at the same point of time. |
||||||
|
* Supposing server's tick rate is 30, but `Timer` is set to emit a signal |
||||||
|
* 60 time per second, then on average it will emit two signals each tick at |
||||||
|
* the same exact time. |
||||||
|
*/ |
||||||
|
|
||||||
|
// Is timer currently tracking time until the next event? |
||||||
|
var private bool isTimerEnabled; |
||||||
|
// Should timer automatically reset after the next event to |
||||||
|
// also generate recurring signals? |
||||||
|
var private bool isTimerAutoReset; |
||||||
|
// Currently elapsed time since this timer has started waiting for the |
||||||
|
// next event |
||||||
|
var private float totalElapsedTime; |
||||||
|
// Time interval between timer's start and generating the signal |
||||||
|
var private float eventInterval; |
||||||
|
// This flag tells `Timer` to stop trying to emit messages that accumulated |
||||||
|
// between two `Tick()` updates. Used in case `Timer` was disables or |
||||||
|
// stopped during one of them. |
||||||
|
var private bool clearEventQueue; |
||||||
|
|
||||||
|
var private Timer_OnElapsed_Signal onElapsedSignal; |
||||||
|
|
||||||
|
protected function Constructor() |
||||||
|
{ |
||||||
|
onElapsedSignal = Timer_OnElapsed_Signal( |
||||||
|
_.memory.Allocate(class'Timer_OnElapsed_Signal')); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
_.memory.Free(onElapsedSignal); |
||||||
|
StopMe(); // Disconnects from listening to `_.unreal.OnTick()` |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Signal that will be emitted every time timer's interval is elapsed. |
||||||
|
* |
||||||
|
* [Signature] |
||||||
|
* void <slot>(Timer source) |
||||||
|
* |
||||||
|
* @param source `Timer` that emitted the signal. |
||||||
|
*/ |
||||||
|
/* SIGNAL */ |
||||||
|
public final function Timer_OnElapsed_Slot OnElapsed(AcediaObject receiver) |
||||||
|
{ |
||||||
|
return Timer_OnElapsed_Slot(onElapsedSignal.NewSlot(receiver)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method is called every tick while the caller `Timer` is running and |
||||||
|
* can be overloaded to modify how passed time is affected by the |
||||||
|
* time dilation. |
||||||
|
* |
||||||
|
* For example, to make caller `Timer` count real time you need to passed time |
||||||
|
* by the `dilationCoefficient`: |
||||||
|
* `return timeDelta / dilationCoefficient;`. |
||||||
|
* |
||||||
|
* @param timeDelta In-game time that has passed since the last |
||||||
|
* `Tick()` event. To obtain real time should be divided by |
||||||
|
* `dilationCoefficient`. |
||||||
|
* @param dilationCoefficient Coefficient of time dilation for the passed |
||||||
|
* `Tick()`. Regular speed is `1.0` (corrected from native value `1.1` |
||||||
|
* for Unreal Engine 2). |
||||||
|
*/ |
||||||
|
protected function float HandleTimeDilation( |
||||||
|
float timeDelta, |
||||||
|
float dilationCoefficient) |
||||||
|
{ |
||||||
|
return timeDelta; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns current interval between `OnElapsed()` signals for the |
||||||
|
* caller `Timer`. In seconds. |
||||||
|
* |
||||||
|
* @return How many seconds separate two `OnElapsed()` signals |
||||||
|
* (or starting a timer and next `OnElapsed()` event). |
||||||
|
*/ |
||||||
|
public final function float GetInterval() |
||||||
|
{ |
||||||
|
return eventInterval; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets current interval between `OnElapsed()` signals for the |
||||||
|
* caller `Timer`. In seconds. |
||||||
|
* |
||||||
|
* Setting this value while the caller `Timer` is running resets it (same as |
||||||
|
* calling `StopMe().Start()`). |
||||||
|
* |
||||||
|
* @param newInterval How many seconds should separate two `OnElapsed()` |
||||||
|
* signals (or starting a timer and next `OnElapsed()` event)? |
||||||
|
* Setting a value `<= 0` disables the timer. |
||||||
|
* @return Caller `Timer` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function Timer SetInterval(float newInterval) |
||||||
|
{ |
||||||
|
eventInterval = newInterval; |
||||||
|
if (eventInterval <= 0) |
||||||
|
{ |
||||||
|
StopMe(); |
||||||
|
return self; |
||||||
|
} |
||||||
|
if (isTimerEnabled) { |
||||||
|
Start(); |
||||||
|
} |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether the timer is currently enabled (emitting signals with |
||||||
|
* set interval). |
||||||
|
* |
||||||
|
* @return `true` if caller `Timer` is enabled and `false` otherwise. |
||||||
|
*/ |
||||||
|
public final function bool IsEnabled() |
||||||
|
{ |
||||||
|
return isTimerEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether this `Timer` would automatically reset after the emitted |
||||||
|
* `OnElapsed()` signal, allowing for recurring signals. |
||||||
|
* |
||||||
|
* @return `true` if `Timer` will emit `OnElapse()` signal each time |
||||||
|
* the interval elapses and `false` otherwise. |
||||||
|
*/ |
||||||
|
public final function bool IsAutoReset(float newInterval) |
||||||
|
{ |
||||||
|
return isTimerAutoReset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets whether this `Timer` would automatically reset after the emitted |
||||||
|
* `OnElapsed()` signal, allowing for recurring signals. |
||||||
|
* |
||||||
|
* @param doAutoReset `true` if `Timer` will emit `OnElapse()` signal |
||||||
|
* each time the interval elapses and `false` otherwise. |
||||||
|
* @return Caller `Timer` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function Timer SetAutoReset(bool doAutoReset) |
||||||
|
{ |
||||||
|
isTimerAutoReset = doAutoReset; |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts emitting `OneElapsed()` signal. |
||||||
|
* |
||||||
|
* Does nothing if current timer interval (set by `SetInterval()`) is set |
||||||
|
* to a value that's `<= 0`. |
||||||
|
* |
||||||
|
* If caller `Timer` is already running, resets it (same as calling |
||||||
|
* `StopMe().Start()`). |
||||||
|
* |
||||||
|
* @return Caller `Timer` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function Timer Start() |
||||||
|
{ |
||||||
|
if (eventInterval <= 0) { |
||||||
|
return self; |
||||||
|
} |
||||||
|
if (!isTimerEnabled) { |
||||||
|
_.unreal.OnTick(self).connect = Tick; |
||||||
|
} |
||||||
|
isTimerEnabled = true; |
||||||
|
totalElapsedTime = 0.0; |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stops emitting `OneElapsed()` signal. |
||||||
|
* |
||||||
|
* @return Caller `Timer` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function Timer StopMe() |
||||||
|
{ |
||||||
|
_.unreal.OnTick(self).Disconnect(); |
||||||
|
isTimerEnabled = false; |
||||||
|
clearEventQueue = true; |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns currently elapsed time since caller `Timer` has started waiting for |
||||||
|
* the next event. |
||||||
|
* |
||||||
|
* @return Elapsed time since caller `Timer` has started. |
||||||
|
*/ |
||||||
|
public final function float GetElapsedTime() |
||||||
|
{ |
||||||
|
return totalElapsedTime; |
||||||
|
} |
||||||
|
|
||||||
|
private final function Tick(float delta, float dilationCoefficient) |
||||||
|
{ |
||||||
|
if (onElapsedSignal == none || eventInterval <= 0.0) |
||||||
|
{ |
||||||
|
StopMe(); |
||||||
|
return; |
||||||
|
} |
||||||
|
totalElapsedTime += HandleTimeDilation(delta, dilationCoefficient); |
||||||
|
clearEventQueue = false; |
||||||
|
while (totalElapsedTime > eventInterval && !clearEventQueue) |
||||||
|
{ |
||||||
|
// It is important to modify _before_ the signal call in case `Timer` |
||||||
|
// is reset there and already has a zeroed `totalElapsedTime` |
||||||
|
totalElapsedTime -= eventInterval; |
||||||
|
onElapsedSignal.Emit(self); |
||||||
|
if (!isTimerAutoReset) |
||||||
|
{ |
||||||
|
StopMe(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
Loading…
Reference in new issue