GameClock — the runtime clock, and the one shared handle consumers reference. A GameClock is a self-contained Resource: it holds the canonical integer microsecond count, the calendar that gives that count meaning, the signal bus, its own time serializer, its own age service, AND it owns all clock-boundary event emission. UI, lighting, the day/night service, and the age service each @export one GameClock (a shared .tres) and read it directly:
- clock.date_time() → the current DateTime (derived)
- clock.signal_bus.<event> → subscribe to clock-boundary events
- clock.get_time_snapshot_serializer() → save/load the clock itself
- clock.to_dict() / clock.from_dict(...) → convenience delegates
- clock.age_service → the counter for this clock
Dependency direction (kept honest): GameClock ──► TimeSignalBus ──► consumers (UI, lighting, DayNight, …)
│
└─► clock.age_service (counter, optional — see AgeService header) The clock does NOT depend on its consumers, services, or the TimeHost. Services (like AgeService) and consumers subscribe to clock.signal_bus; the host drives clock.advance_microseconds but does not mediate them. Every mutation goes through advance_microseconds and fires the appropriate events on the clock's own bus: game_seconds_advanced, date_time_changed, date_changed (on day-boundary crossings), day_finished, and event_day_started. There is exactly one emit path — the clock — so a driver (the TimeHost, a replay system, a save-load, a unit test) cannot desynchronise consumers by forgetting to emit. date_time() is derived from current_microseconds via the calendar — it is never stored, never stale. You can have as many GameClocks as you like (a world clock, a separate festival timeline at a different rate). Each clock is independent and self-contained. Multi-clock games use a ClockGroup Resource to save / load all clocks in one dict — see clock_group.gd.
Source: addons/calendar_time/game_time/state/game_clock.gd
Syntax
class GameClock extends ResourceMembers
| Name | Kind | Summary |
|---|---|---|
current_microseconds | Property | The canonical clock value: an exact integer count of elapsed game-microseconds. Source of truth. Everything else (date_time, game_seconds) is derived from it. Not @export — it's runtime state, not authored data; saves go through TimeSnapshot. |
speed_multiplier | Property | Runtime speed multiplier applied when a TimeHost/ClockGroup drives this clock. This lets one timeline run faster/slower than another without changing global Engine.time_scale or every clock on the host. Direct calls to advance_microseconds() remain exact and unscaled for deterministic seams. Pause/resume semantics: any value `<= 0.0` is a pause from the consumer's point of view. The host/ClockGroup sees the zero and skips `advance_scaled_microseconds` for this clock, so clock-boundary events stop firing while paused. UI/animation consumers that read `clock.date_time()` directly are intentionally not gated here — if a game wants to also freeze presentation, it does that with its own time-scale or by watching `clock_speed_changed` and pausing its own tween/animation systems. The `clock_speed_changed` signal is emitted on every actual change (including pause and resume), so observers can bridge the clock's pause to their own runtime state without polling. |
calendar | Property | The calendar that defines the shape of time (months, days, event days) and the date math + rate (calendar.get_scale()). Authored on the clock resource. |
time_scale | Property | Optional per-clock rate override: how many game-seconds one real-second becomes. Leave unset to use the calendar/default time scale fallback. Hosts can still propagate their authored time scale to each clock in a group so the host remains the main editor surface. |
epoch | Property | Optional reference date used as the zero-point for date_time derivation. date_time() returns calendar.advance_date_time(epoch, elapsed_seconds). Leave unset to use DateTime.new(), the calendar epoch fallback. |
clock_id_override | Property | Stable identity for save/load. Optional — empty by default; resolved lazily via `clock_id` to a hash of resource_path (when the resource is loaded from a file) plus a deterministic runtime counter fallback. Used by `ClockGroupSerializer` to key per-clock save data instead of array index, so reordering the host's clocks between save and load doesn't cross-pollinate state. Two clones of the same `.tres` get the same id automatically (no user typing required). Different `.tres` files always get different ids. Two `GameClock.new()` calls (no resource_path) get distinct ids from a monotonic counter. Override `clock_id = "..."` for explicit stable cross-machine identity (e.g. to dedupe clocks that share a resource_path across multiple instances). |
signal_bus | Property | The clock event bus. Created eagerly so a consumer can subscribe the moment it holds the clock — before the host has finished _ready. The clock itself emits all clock-boundary events on this bus from `advance_microseconds`. |
age_service | Property | The age service bound to this clock's time. Optional. Setter wires `age_service.clock = self` so the counter subscribes to this clock's bus. Not @export — the Godot parser fails to resolve a custom-Resource @export on a Resource when the two classes live in the same plugin folder (forward-reference parse order). The game assigns this from the clock .tres (plain property write) or at runtime; the setter still wires `clock = self` so the counter subscribes correctly. |
time_snapshot_serializer | Property | The clock's own time serializer (constructed in _init; self-wired). |
age_registry | Property | The clock's own age registry. Each `GameClock` carries its own `AgeStateRegistry` so age state is hermetically scoped to the clock: `AgeService`s and `AgeComponent`s attached to this clock read/write here. No process singleton — two clocks can run side-by-side without seeing each other's age states. Constructed in `_init`; lifetime matches the clock resource. |
clock_id | Property | Stable id used by ClockGroupSerializer for save/load identity. Lazy-derives from `clock_id_override` → resource_path → a monotonic counter (see `_derive_clock_id` for the exact priority chain). Once resolved, the value is cached — re-deriving only happens if `clock_id_override` is reassigned, which clears the cache. |
path | Property | |
seed | Property | |
seed_str | Property | |
h | Property | |
advance_microseconds | Method | |
bus | Property | |
old_dt | Property | |
new_dt | Property | |
seconds_advanced | Property | |
days_between | Property | |
new_event | Property | |
old_event | Property | |
advance_scaled_microseconds | Method | |
progress_time | Method | |
advance_to_next_day | Method | |
old_dt | Property | |
target_dt | Property | |
delta_seconds | Property | |
publish_current_state | Method | |
dt | Property | |
publish_state_loaded | Method | |
get_time_snapshot_serializer | Method | |
to_dict | Method | |
from_dict | Method | |
date_time | Method | |
game_seconds | Method |
Source
addons/calendar_time/game_time/state/game_clock.gd
Plugin docs root:gdscript/plugins/calendar_time_dev/docs