Browse Source

Add timers and `TimeAPI`

pull/8/head
Anton Tarasenko 4 years ago
parent
commit
3458bd202f
  1. 2
      sources/Global.uc
  2. 38
      sources/Time/Events/Timer_OnElapsed_Signal.uc
  3. 40
      sources/Time/Events/Timer_OnElapsed_Slot.uc
  4. 32
      sources/Time/RealTimer.uc
  5. 108
      sources/Time/TimeAPI.uc
  6. 248
      sources/Time/Timer.uc

2
sources/Global.uc

@ -30,6 +30,7 @@ var public BoxAPI box;
var public LoggerAPI logger;
var public CollectionsAPI collections;
var public UnrealAPI unreal;
var public TimeAPI time;
var public AliasesAPI alias;
var public TextAPI text;
var public MemoryAPI memory;
@ -60,6 +61,7 @@ protected function Initialize()
text = TextAPI(memory.Allocate(class'TextAPI'));
collections = CollectionsAPI(memory.Allocate(class'CollectionsAPI'));
unreal = UnrealAPI(memory.Allocate(class'UnrealAPI'));
time = TimeAPI(memory.Allocate(class'TimeAPI'));
logger = LoggerAPI(memory.Allocate(class'LoggerAPI'));
alias = AliasesAPI(memory.Allocate(class'AliasesAPI'));
console = ConsoleAPI(memory.Allocate(class'ConsoleAPI'));

38
sources/Time/Events/Timer_OnElapsed_Signal.uc

@ -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'
}

40
sources/Time/Events/Timer_OnElapsed_Slot.uc

@ -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
{
}

32
sources/Time/RealTimer.uc

@ -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
{
}

108
sources/Time/TimeAPI.uc

@ -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
{
}

248
sources/Time/Timer.uc

@ -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…
Cancel
Save