Browse Source

Refactor `TimeAPI`, add it to client side

pull/8/head
Anton Tarasenko 2 years ago
parent
commit
33577660d9
  1. 17
      sources/ClientRealm/API/Unreal/ClientUnrealAPI.uc
  2. 1
      sources/ClientRealm/ClientAcediaAdapter.uc
  3. 1
      sources/ClientRealm/ClientGlobal.uc
  4. 0
      sources/CoreRealm/API/Time/Events/Timer_OnElapsed_Signal.uc
  5. 0
      sources/CoreRealm/API/Time/Events/Timer_OnElapsed_Slot.uc
  6. 46
      sources/CoreRealm/API/Time/TimeAPI.uc
  7. 138
      sources/CoreRealm/API/Time/Timer.uc
  8. 2
      sources/CoreRealm/API/UnrealAPI/UnrealAPI.uc
  9. 1
      sources/CoreRealm/AcediaAdapter.uc
  10. 2
      sources/CoreRealm/CoreGlobal.uc
  11. 0
      sources/KFRealm/Core/ActorStalker.uc
  12. 2
      sources/KFRealm/Core/Time/KF1_RealTimer.uc
  13. 28
      sources/KFRealm/Core/Time/KF1_TimeAPI.uc
  14. 181
      sources/KFRealm/Core/Time/KF1_Timer.uc
  15. 23
      sources/ServerRealm/API/Unreal/ServerUnrealAPI.uc
  16. 3
      sources/ServerRealm/ServerAcediaAdapter.uc
  17. 4
      sources/ServerRealm/ServerGlobal.uc

17
sources/ClientRealm/API/Unreal/ClientUnrealAPI.uc

@ -25,17 +25,18 @@ var protected bool initialized;
var public InteractionAPI interaction;
public function Initialize(class<ClientAcediaAdapter> adapterClass)
public function Initialize(class<AcediaAdapter> adapterClass)
{
if (initialized) {
return;
}
local class<ClientAcediaAdapter> asClientAdapter;
if (initialized) return;
asClientAdapter = class<ClientAcediaAdapter>(adapterClass);
if (asClientAdapter == none) return;
super.Initialize(adapterClass);
initialized = true;
if (adapterClass == none) {
return;
}
interaction = InteractionAPI(_.memory.Allocate(
adapterClass.default.clientInteractionAPIClass));
asClientAdapter.default.clientInteractionAPIClass));
}
/**

1
sources/ClientRealm/ClientAcediaAdapter.uc

@ -27,6 +27,7 @@ var public const class<InteractionAPI> clientInteractionAPIClass;
defaultproperties
{
timeAPIClass = class'KF1_TimeAPI'
clientUnrealAPIClass = class'KF1_ClientUnrealAPI'
clientInteractionAPIClass = class'KF1_InteractionAPI'
}

1
sources/ClientRealm/ClientGlobal.uc

@ -68,6 +68,7 @@ protected function Initialize()
unreal = ClientUnrealAPI(
_.memory.Allocate(clientAdapterClass.default.clientUnrealAPIClass));
unreal.Initialize(clientAdapterClass);
time.Initialize(unreal);
// Create `AcediaInteraction`
localPlayer = unreal.GetLocalPlayer();
if (localPlayer != none)

0
sources/ServerRealm/API/Time/Events/Timer_OnElapsed_Signal.uc → sources/CoreRealm/API/Time/Events/Timer_OnElapsed_Signal.uc

0
sources/ServerRealm/API/Time/Events/Timer_OnElapsed_Slot.uc → sources/CoreRealm/API/Time/Events/Timer_OnElapsed_Slot.uc

46
sources/ServerRealm/API/Time/ServerTimeAPI.uc → sources/CoreRealm/API/Time/TimeAPI.uc

@ -17,9 +17,28 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class ServerTimeAPI extends AcediaObject
class TimeAPI extends AcediaObject
abstract;
// API to use to initialize `Timer`s, should be chosen depending on where
// `Timer`s are created.
var protected UnrealAPI api;
/**
* Initializes caller `TimeAPI` with given `UnrealAPI` instance that will
* be used to create all `Timer`s.
*
* This is necessary, because we don't know where `TimeAPI` will be used:
* on server or on client.
*/
public function Initialize(UnrealAPI newAPI)
{
if (api != none) {
return;
}
api = newAPI;
}
/**
* Creates new `Timer`. Does not start it.
*
@ -49,33 +68,34 @@ public function Timer NewTimer(
public function Timer StartTimer(float interval, optional bool autoReset);
/**
* Creates new `RealTimer`. Does not start it.
* Creates new `Timer` that measures real time, not in-game one.
* Does not start it.
*
* @param interval Returned `RealTimer` will be configured to emit
* @param interval Returned `Timer` will be configured to emit
* `OnElapsed()` signals every `interval` seconds.
* @param autoReset `true` will configure caller `RealTimer` to repeatedly
* @param autoReset `true` will configure caller `Timer` 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`
* 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 function RealTimer NewRealTimer(
public function Timer NewRealTimer(
optional float interval,
optional bool autoReset);
/**
* Creates and starts new `RealTimer`.
* Creates and starts new `Timer` that measures real time, not in-game one.
*
* @param interval Returned `RealTimer` will be configured to emit
* @param interval Returned `Timer` will be configured to emit
* `OnElapsed()` signals every `interval` seconds.
* @param autoReset Setting this to `true` will configure caller `RealTimer`
* @param autoReset Setting this to `true` will configure caller `Timer`
* to repeatedly emit `OnElapsed()` signal every `interval` seconds,
* `false` (default value) will make returned `RealTimer` emit that signal
* `false` (default value) will make returned `Timer` emit that signal
* only once.
* @return `RealTimer`, configured to emit `OnElapsed()` every `interval`
* @return `Timer`, configured to emit `OnElapsed()` every `interval`
* seconds. Guaranteed to be not `none`.
*/
public function RealTimer StartRealTimer(
public function Timer StartRealTimer(
float interval,
optional bool autoReset);

138
sources/ServerRealm/API/Time/Timer.uc → sources/CoreRealm/API/Time/Timer.uc

@ -1,7 +1,7 @@
/**
* Timer class that generates a signal after a set interval, with an option
* to generate recurring signals.
* Copyright 2021 Anton Tarasenko
* Copyright 2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -18,7 +18,8 @@
* 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;
class Timer extends AcediaObject
abstract;
/**
* Because the `Timer` depends on the `Tick()` event, it has the same
@ -31,35 +32,6 @@ class Timer extends AcediaObject;
* 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 `_server.unreal.OnTick()`
}
/**
* Signal that will be emitted every time timer's interval is elapsed.
*
@ -69,10 +41,7 @@ protected function Finalizer()
* @param source `Timer` that emitted the signal.
*/
/* SIGNAL */
public final function Timer_OnElapsed_Slot OnElapsed(AcediaObject receiver)
{
return Timer_OnElapsed_Slot(onElapsedSignal.NewSlot(receiver));
}
public function Timer_OnElapsed_Slot OnElapsed(AcediaObject receiver);
/**
* This method is called every tick while the caller `Timer` is running and
@ -92,10 +61,7 @@ public final function Timer_OnElapsed_Slot OnElapsed(AcediaObject receiver)
*/
protected function float HandleTimeDilation(
float timeDelta,
float dilationCoefficient)
{
return timeDelta;
}
float dilationCoefficient);
/**
* Returns current interval between `OnElapsed()` signals for the
@ -104,10 +70,7 @@ protected function float HandleTimeDilation(
* @return How many seconds separate two `OnElapsed()` signals
* (or starting a timer and next `OnElapsed()` event).
*/
public final function float GetInterval()
{
return eventInterval;
}
public function float GetInterval();
/**
* Sets current interval between `OnElapsed()` signals for the
@ -121,19 +84,7 @@ public final function float GetInterval()
* 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;
}
public function Timer SetInterval(float newInterval);
/**
* Checks whether the timer is currently enabled (emitting signals with
@ -141,10 +92,7 @@ public final function Timer SetInterval(float newInterval)
*
* @return `true` if caller `Timer` is enabled and `false` otherwise.
*/
public final function bool IsEnabled()
{
return isTimerEnabled;
}
public function bool IsEnabled();
/**
* Checks whether this `Timer` would automatically reset after the emitted
@ -153,10 +101,7 @@ public final function bool IsEnabled()
* @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;
}
public function bool IsAutoReset(float newInterval);
/**
* Sets whether this `Timer` would automatically reset after the emitted
@ -166,11 +111,7 @@ public final function bool IsAutoReset(float newInterval)
* 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;
}
public function Timer SetAutoReset(bool doAutoReset);
/**
* Starts emitting `OneElapsed()` signal.
@ -183,31 +124,14 @@ public final function Timer SetAutoReset(bool doAutoReset)
*
* @return Caller `Timer` to allow for method chaining.
*/
public final function Timer Start()
{
if (eventInterval <= 0) {
return self;
}
if (!isTimerEnabled) {
_server.unreal.OnTick(self).connect = Tick;
}
isTimerEnabled = true;
totalElapsedTime = 0.0;
return self;
}
public function Timer Start();
/**
* Stops emitting `OneElapsed()` signal.
*
* @return Caller `Timer` to allow for method chaining.
*/
public final function Timer StopMe()
{
_server.unreal.OnTick(self).Disconnect();
isTimerEnabled = false;
clearEventQueue = true;
return self;
}
public function Timer StopMe();
/**
* Returns currently elapsed time since caller `Timer` has started waiting for
@ -215,43 +139,9 @@ public final function Timer StopMe()
*
* @return Elapsed time since caller `Timer` has started.
*/
public final function float GetElapsedTime()
{
return totalElapsedTime;
}
private final function Tick(float delta, float dilationCoefficient)
{
local int lifeVersion;
public function float GetElapsedTime();
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;
// Stop `Timer` before emitting a signal, to allow user to potentially
// restart it
if (!isTimerAutoReset) {
StopMe();
}
// During signal emission caller `Timer` can get reallocated and
// used to perform a completely different role.
// In such a case we need to bail from this method as soom as
// possible.
lifeVersion = GetLifeVersion();
onElapsedSignal.Emit(self);
if (!isTimerEnabled || lifeVersion != GetLifeVersion()) {
return;
}
}
}
private function Tick(float delta, float dilationCoefficient);
defaultproperties
{

2
sources/CoreRealm/API/UnrealAPI/UnrealAPI.uc

@ -21,6 +21,8 @@
class UnrealAPI extends AcediaObject
abstract;
public function Initialize(class<AcediaAdapter> adapterClass);
/**
* Signal that will be emitted every tick.
*

1
sources/CoreRealm/AcediaAdapter.uc

@ -36,6 +36,7 @@ class AcediaAdapter extends AcediaObject
*/
var public const class<SideEffectAPI> sideEffectAPIClass;
var public const class<TimeAPI> timeAPIClass;
defaultproperties
{

2
sources/CoreRealm/CoreGlobal.uc

@ -25,6 +25,7 @@ var protected bool initialized;
var protected class<AcediaAdapter> adapterClass;
var public SideEffectAPI sideEffects;
var public TimeAPI time;
var private LoggerAPI.Definition fatNoAdapterClass;
@ -52,6 +53,7 @@ protected function Initialize()
api = class'Global'.static.GetInstance().memory;
sideEffects =
SideEffectAPI(api.Allocate(adapterClass.default.sideEffectAPIClass));
time = TimeAPI(api.Allocate(adapterClass.default.timeAPIClass));
}
/**

0
sources/KFRealm/Server/Unreal/ActorStalker.uc → sources/KFRealm/Core/ActorStalker.uc

2
sources/ServerRealm/API/Time/RealTimer.uc → sources/KFRealm/Core/Time/KF1_RealTimer.uc

@ -18,7 +18,7 @@
* 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;
class KF1_RealTimer extends KF1_Timer;
protected function float HandleTimeDilation(
float timeDelta,

28
sources/KFRealm/Server/Time/KF1_ServerTimeAPI.uc → sources/KFRealm/Core/Time/KF1_TimeAPI.uc

@ -1,5 +1,5 @@
/**
* Acedia's default `ServerTimeAPI` API implementation
* Acedia's default `TimeAPI` API implementation
* Copyright 2021-2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
@ -17,45 +17,45 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class KF1_ServerTimeAPI extends ServerTimeAPI;
class KF1_TimeAPI extends TimeAPI;
public function Timer NewTimer(
optional float interval,
optional bool autoReset)
{
return Timer(_.memory.Allocate(class'Timer'))
return KF1_Timer(_.memory.Allocate(class'KF1_Timer'))
.Initialize(api)
.SetInterval(interval)
.SetAutoReset(autoReset);
}
public function Timer StartTimer(float interval, optional bool autoReset)
{
return Timer(_.memory.Allocate(class'Timer'))
return KF1_Timer(_.memory.Allocate(class'KF1_Timer'))
.Initialize(api)
.SetInterval(interval)
.SetAutoReset(autoReset)
.Start();
}
public function RealTimer NewRealTimer(
public function Timer NewRealTimer(
optional float interval,
optional bool autoReset)
{
local RealTimer newTimer;
newTimer = RealTimer(_.memory.Allocate(class'RealTimer'));
newTimer.SetInterval(interval).SetAutoReset(autoReset);
return newTimer;
return KF1_Timer(_.memory.Allocate(class'KF1_RealTimer'))
.Initialize(api)
.SetInterval(interval).SetAutoReset(autoReset);
}
public function RealTimer StartRealTimer(
public function Timer StartRealTimer(
float interval,
optional bool autoReset)
{
local RealTimer newTimer;
newTimer = RealTimer(_.memory.Allocate(class'RealTimer'));
newTimer.SetInterval(interval)
return KF1_Timer(_.memory.Allocate(class'KF1_RealTimer'))
.Initialize(api)
.SetInterval(interval)
.SetAutoReset(autoReset)
.Start();
return newTimer;
}
defaultproperties

181
sources/KFRealm/Core/Time/KF1_Timer.uc

@ -0,0 +1,181 @@
/**
* Acedia's default implementation for `Timer` class.
* Copyright 2021-2022 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 KF1_Timer extends Timer;
// Is timer currently tracking time until the next event?
var protected bool isTimerEnabled;
// Should timer automatically reset after the next event to
// also generate recurring signals?
var protected bool isTimerAutoReset;
// Currently elapsed time since this timer has started waiting for the
// next event
var protected float totalElapsedTime;
// Time interval between timer's start and generating the signal
var protected 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 protected bool clearEventQueue;
// `UnrealAPI` to use, has to be set to access `OnTick()` signal.
var protected UnrealAPI api;
var protected 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 `api.OnTick()`
api = none;
}
/**
* Initializes caller `Timer` with given `UnrealAPI` instance that will be used
* to track `OnTick()` signal.
*
* This is necessary, because we don't know where `KF1_Timer` will be used:
* on server or on client.
*/
public function Timer Initialize(UnrealAPI newAPI)
{
if (api != none) {
return self;
}
api = newAPI;
return self;
}
/* SIGNAL */
public function Timer_OnElapsed_Slot OnElapsed(AcediaObject receiver)
{
return Timer_OnElapsed_Slot(onElapsedSignal.NewSlot(receiver));
}
protected function float HandleTimeDilation(
float timeDelta,
float dilationCoefficient)
{
return timeDelta;
}
public function float GetInterval()
{
return eventInterval;
}
public function Timer SetInterval(float newInterval)
{
eventInterval = newInterval;
if (eventInterval <= 0)
{
StopMe();
return self;
}
if (isTimerEnabled) {
Start();
}
return self;
}
public function bool IsEnabled()
{
return isTimerEnabled;
}
public function bool IsAutoReset(float newInterval)
{
return isTimerAutoReset;
}
public function Timer SetAutoReset(bool doAutoReset)
{
isTimerAutoReset = doAutoReset;
return self;
}
public function Timer Start()
{
if (eventInterval <= 0) {
return self;
}
if (!isTimerEnabled) {
api.OnTick(self).connect = Tick;
}
isTimerEnabled = true;
totalElapsedTime = 0.0;
return self;
}
public function Timer StopMe()
{
api.OnTick(self).Disconnect();
isTimerEnabled = false;
clearEventQueue = true;
return self;
}
public function float GetElapsedTime()
{
return totalElapsedTime;
}
private function Tick(float delta, float dilationCoefficient)
{
local int lifeVersion;
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;
// Stop `Timer` before emitting a signal, to allow user to potentially
// restart it
if (!isTimerAutoReset) {
StopMe();
}
// During signal emission caller `Timer` can get reallocated and
// used to perform a completely different role.
// In such a case we need to bail from this method as soom as
// possible.
lifeVersion = GetLifeVersion();
onElapsedSignal.Emit(self);
if (!isTimerEnabled || lifeVersion != GetLifeVersion()) {
return;
}
}
}
defaultproperties
{
}

23
sources/ServerRealm/API/Unreal/ServerUnrealAPI.uc

@ -28,23 +28,24 @@ var public GameRulesAPI gameRules;
var public BroadcastAPI broadcasts;
var public InventoryAPI inventory;
public function Initialize(class<ServerAcediaAdapter> adapterClass)
public function Initialize(class<AcediaAdapter> adapterClass)
{
if (initialized) {
return;
}
local class<ServerAcediaAdapter> asServerAdapter;
if (initialized) return;
asServerAdapter = class<ServerAcediaAdapter>(adapterClass);
if (asServerAdapter == none) return;
super.Initialize(adapterClass);
initialized = true;
if (adapterClass == none) {
return;
}
mutator = MutatorAPI(_.memory.Allocate(
adapterClass.default.serverMutatorAPIClass));
asServerAdapter.default.serverMutatorAPIClass));
gameRules = GameRulesAPI(_.memory.Allocate(
adapterClass.default.serverGameRulesAPIClass));
asServerAdapter.default.serverGameRulesAPIClass));
broadcasts = BroadcastAPI(_.memory.Allocate(
adapterClass.default.serverBroadcastAPIClass));
asServerAdapter.default.serverBroadcastAPIClass));
inventory = InventoryAPI (_.memory.Allocate(
adapterClass.default.serverInventoryAPIClass));
asServerAdapter.default.serverInventoryAPIClass));
}
defaultproperties

3
sources/ServerRealm/ServerAcediaAdapter.uc

@ -22,7 +22,6 @@
class ServerAcediaAdapter extends AcediaAdapter
abstract;
var public const class<ServerTimeAPI> serverTimeAPIClass;
var public const class<ServerUnrealAPI> serverUnrealAPIClass;
var public const class<BroadcastAPI> serverBroadcastAPIClass;
var public const class<GameRulesAPI> serverGameRulesAPIClass;
@ -31,7 +30,7 @@ var public const class<MutatorAPI> serverMutatorAPIClass;
defaultproperties
{
serverTimeAPIClass = class'KF1_ServerTimeAPI'
timeAPIClass = class'KF1_TimeAPI'
serverUnrealAPIClass = class'KF1_ServerUnrealAPI'
serverBroadcastAPIClass = class'KF1_BroadcastAPI'
serverGameRulesAPIClass = class'KF1_GameRulesAPI'

4
sources/ServerRealm/ServerGlobal.uc

@ -27,7 +27,6 @@ var protected ServerGlobal myself;
var public KFFrontend kf;
var public ServerUnrealAPI unreal;
var public ServerTimeAPI time;
var private LoggerAPI.Definition fatBadAdapterClass;
@ -67,8 +66,7 @@ protected function Initialize()
unreal = ServerUnrealAPI(
_.memory.Allocate(serverAdapterClass.default.serverUnrealAPIClass));
unreal.Initialize(serverAdapterClass);
time = ServerTimeAPI(
_.memory.Allocate(serverAdapterClass.default.serverTimeAPIClass));
time.Initialize(unreal);
kf = KFFrontend(_.memory.Allocate(class'KF1_Frontend'));
}

Loading…
Cancel
Save