Clocks
Games run several clocks at once: the world time of the engine, real time, the server time that every client follows, a time of day. When every plugin picks one on its own, a pause stops crafting but not the cool downs, and slow motion reaches the water but not the quest timer. Pulse Foundation has three clocks, and every Pulse plugin asks for the time by clock.
| Clock | Who leads it | Pauses | Use it for |
|---|---|---|---|
| Game | The server: a time, a rate and a pause, the same on every machine | Yes, on every machine | Gameplay: crafting, cool downs, regrowing, quest timers, the time of day |
| Server | The world time of the server, on every machine (server time) | Only with the world | Deterministic things that never pause: water, waves |
| Real | This machine | Never | Interface: animations, notifications |
Without a network (and on a listen server) the server time is the world time, so the game clock also follows the pause and slow motion of the world. In a multiplayer game the world keeps running for everyone; pausing the game clock stops everything that runs on it on every machine at the same moment.
Reading the time
C++: UPulseClockSubsystem::Get(this)->GetTime(EPulseClock::Game). Blueprint: Get Pulse Clock Time (any world context). The clock subsystem exists in every game and PIE world.
if (const UPulseClockSubsystem* clocks = UPulseClockSubsystem::Get(this))
{
const double now = clocks->GetTime(EPulseClock::Game);
}
Points in time and timers
Keep the moment something ends, not the seconds left. An FPulseClockStamp is a point on one clock: Make Pulse Clock Stamp (clock, seconds from now), Get Pulse Clock Remaining, Has Pulse Clock Stamp Passed. A stamp on the game clock stays right while its actor is unloaded, while a save waits on disk and while the game is paused: a resource node that regrows “at game time 5230” regrows then, whenever its cell loads.
Stamps are plain data with SaveGame fields; save them with your component. A stamp that is not set (IsSet() == false) has always passed.
Set Pulse Clock Timer (C++ SetTimer with an FSimpleDelegate) calls back once its stamp is reached. Timers are checked every frame on the machine that set them; a game clock that jumps past a timer calls it right away, a paused one never. Timers are not replicated or saved: save the stamp and set the timer again after loading. Clear Pulse Clock Timer removes one.
const FPulseClockStamp regrowAt = clocks->MakeStamp(EPulseClock::Game, 300.0);
RegrowTimer = clocks->SetTimer(regrowAt, FSimpleDelegate::CreateUObject(this, &AMyNode::Regrow), TEXT("Regrow"));
Leading the game clock
On the server: Set Game Time (jump), Set Game Rate (game seconds per server second, 0.5 = slow motion) and Set Game Paused. Only the state replicates, and only when it changes (APulseClockState, spawned by the subsystem, push model); every client computes the game time from it and its own server time.
Saves keep the game clock with Save Game Clock / Load Game Clock (FPulseGameClockSave: time, rate, pause); the server time starts again with every session.
Messages
Every machine broadcasts the changes of its clocks on the message bus (payload FPulseClockMessage: clock, change, old and new time, rate, pause), and on UPulseClockSubsystem::OnClockChangedNative:
| Channel | When |
|---|---|
Pulse.Message.Clock.Jumped | The game time was set or loaded, or the server time of a client caught up after a hitch (clock Server). Amount = seconds jumped |
Pulse.Message.Clock.Paused | The game clock stopped |
Pulse.Message.Clock.Resumed | The game clock runs again |
Pulse.Message.Clock.RateChanged | The rate changed. Amount = the new rate |
Listen to Pulse.Message.Clock for all of them. A client that joins takes the game clock as it is, without a message.
Debugging
The Pulse Debugger and PulseDebugDump Clock show the three clocks, the game clock state and the timers waiting on this machine. Cheats (Console): PulseClockInfo, PulseClockPause [0|1], PulseClockRate <rate>, PulseClockJump <seconds>.
API
| Function | Where | What it does |
|---|---|---|
GetTime(Clock) | Any | Seconds on a clock |
MakeStamp(Clock, Seconds), GetRemaining(Stamp), HasPassed(Stamp) | Any | Points in time |
SetTimer(Stamp, Callback, Label), ClearTimer(Handle), GetTimer(Handle, Stamp) | Any | Timers of this machine |
SetGameTime, SetGameRate, SetGamePaused | Server | Lead the game clock |
SaveGameClock, LoadGameClock | Server (load) | The game clock in a save |
GetGameClock, IsGamePaused, GetGameRate | Any | The game clock state |
Pulse::Clock::EvaluateGameClock, Reanchor, CompareGameClocks | Any | The pure math, for your own clocks and specs |