[Minor Feature] Expose the simulation clock to Python — getSimulationTime() is C++-only, metrics runtime is wall clock #396

Open
opened 2026-07-27 02:54:51 +00:00 by john · 0 comments
Owner

Problem

The engine keeps a millisecond simulation counter for headless mode at src/GameEngine.h:229
(int simulation_time), readable in C++ via getSimulationTime() (src/GameEngine.h:334). Neither
is reachable from Python.

The only clock Python can currently see is runtime in the metrics dict
(src/McRFPy_API.cpp:1925), which is wall clock. For any use that needs replayable timing —
headless test harnesses, deterministic benchmarks, agent/simulation environments (#55, #154, #156) —
wall clock is the wrong clock, and every timing number collected through it is unreplayable.

Two parts

1. Expose the counter. Python-reachable access to the simulation millisecond counter and to
current_frame. Additive only — do not change runtime's existing meaning, other code depends on it.

2. Per-interval timer semantics on advance. GameEngine::testTimers fires each elapsed timer
once per step, regardless of how many intervals actually elapsed. So advancing 10 seconds in one
wide step fires a 1-second repeating timer once, not ten times.

This is the subtle half. The resulting trace is not corrupt — it is sparse, which is far harder to
notice than a crash and easy to mistake for a real result. A caller needs to be able to advance by
N ms and have every repeating timer fire once per elapsed interval.

Acceptance criterion: a 1-second repeating timer fires exactly 10 times across 10 seconds of
advance.

3. Rendering must cost zero simulated time. renderScene() must not advance simulation_time, so
a screenshot is free with respect to the clock. Currently believed true; needs a test asserting it.

Notes

  • Do not introduce a second clock. The engine counter is canonical; anything Python-side is an adapter.
  • Related: #394 (unseeded behavior RNG — the other half of deterministic replay), #395 (mcrfpy.lock()
    coverage), #55 / #154 / #156 (agent-simulation use cases that need a replayable clock).
## Problem The engine keeps a millisecond simulation counter for headless mode at `src/GameEngine.h:229` (`int simulation_time`), readable in C++ via `getSimulationTime()` (`src/GameEngine.h:334`). **Neither is reachable from Python.** The only clock Python can currently see is `runtime` in the metrics dict (`src/McRFPy_API.cpp:1925`), which is **wall clock**. For any use that needs replayable timing — headless test harnesses, deterministic benchmarks, agent/simulation environments (#55, #154, #156) — wall clock is the wrong clock, and every timing number collected through it is unreplayable. ## Two parts **1. Expose the counter.** Python-reachable access to the simulation millisecond counter and to `current_frame`. Additive only — do not change `runtime`'s existing meaning, other code depends on it. **2. Per-interval timer semantics on advance.** `GameEngine::testTimers` fires each elapsed timer **once per step, regardless of how many intervals actually elapsed**. So advancing 10 seconds in one wide step fires a 1-second repeating timer **once**, not ten times. This is the subtle half. The resulting trace is not corrupt — it is *sparse*, which is far harder to notice than a crash and easy to mistake for a real result. A caller needs to be able to advance by N ms and have every repeating timer fire once per elapsed interval. **Acceptance criterion:** a 1-second repeating timer fires **exactly 10 times** across 10 seconds of advance. **3. Rendering must cost zero simulated time.** `renderScene()` must not advance `simulation_time`, so a screenshot is free with respect to the clock. Currently believed true; needs a test asserting it. ## Notes - Do not introduce a second clock. The engine counter is canonical; anything Python-side is an adapter. - Related: #394 (unseeded behavior RNG — the other half of deterministic replay), #395 (`mcrfpy.lock()` coverage), #55 / #154 / #156 (agent-simulation use cases that need a replayable clock).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
john/McRogueFace#396
No description provided.