Persist step-back history in saves (v12): rewind storms past a loaded moment
Weather is integrated and path-dependent, so reversing it can only restore recorded snapshots -- and a save held only the current moment, so after a load there was no past to step back to and loaded storms froze on backward step. Save bumped to v12: it now appends the most recent wxSaveMax(40) step-back frames (each = humidity/cloud/rain + storms + RNG) after the planet state. loadGame restores them (and still drops any stale pre-load history), so after a load you can step backward ~40 steps and the storms reverse along their saved track. Pre-v12 saves load with no step-back history (you can still step forward then back). Adds ~10 MB to a save (the user chose the short window). All five suites pass; GUI build clean. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7631eb4964
commit
1358e81426
7
BUILD.md
7
BUILD.md
@ -76,9 +76,10 @@ CLI flags (applied before the first load/generate):
|
|||||||
planet.cfg human-editable key=value config of every PlanetConfig parameter;
|
planet.cfg human-editable key=value config of every PlanetConfig parameter;
|
||||||
auto-created on first run, reload live with F2. Range-checked on
|
auto-created on first run, reload live with F2. Range-checked on
|
||||||
load; an invalid file reverts to safe defaults (not overwritten).
|
load; an invalid file reverts to safe defaults (not overwritten).
|
||||||
planet.save binary snapshot (versioned, currently v11: +weather systems/storms; v10
|
planet.save binary snapshot (versioned, currently v12: +step-back history (~40 frames, so a
|
||||||
+weather fields; v9 +moons; v8 +Live World clock; v7 +biota): seed + config +
|
load can rewind storms); v11 +weather systems/storms; v10 +weather fields; v9
|
||||||
full planet state; F5 writes it, F9 reloads and resumes deterministically. As of v6
|
+moons; v8 +Live World clock; v7 +biota): seed + config + full planet state; F5
|
||||||
|
writes it, F9 reloads and resumes deterministically. As of v6
|
||||||
the config is stored as a self-describing key=value block (like
|
the config is stored as a self-describing key=value block (like
|
||||||
planet.cfg), so adding/removing config fields no longer breaks saves
|
planet.cfg), so adding/removing config fields no longer breaks saves
|
||||||
(unknown keys ignored, missing keys default). v6 cannot load pre-v6
|
(unknown keys ignored, missing keys default). v6 cannot load pre-v6
|
||||||
|
|||||||
18
CLAUDE.md
18
CLAUDE.md
@ -427,10 +427,12 @@ Working and verified (logic tested headless):
|
|||||||
snapshots the full state — `Planet::captureWeather()`/`restoreWeather()` (humidity/cloud/rain/
|
snapshots the full state — `Planet::captureWeather()`/`restoreWeather()` (humidity/cloud/rain/
|
||||||
storms/RNG) into a bounded `wxUndo` ring — and **`,` restores the previous snapshot**, so the
|
storms/RNG) into a bounded `wxUndo` ring — and **`,` restores the previous snapshot**, so the
|
||||||
step really reverses *everything* (clouds, rain, **moving storms**) plus the deterministic sky.
|
step really reverses *everything* (clouds, rain, **moving storms**) plus the deterministic sky.
|
||||||
`liveAdvance` records a snapshot at ~one-step cadence on **any** forward advance (continuous run or
|
A manual step **always** records (rate-independent, via `wxPushSnapshot`); a continuous run records
|
||||||
manual step), so storms born during a continuous run also rewind (an earlier version cleared the
|
a throttled snapshot (~1/sec) so a run is rewindable too. `,` searches the ring by time, the ring
|
||||||
history on run, which left run-born storms frozen on step-back); `,` searches the ring by time, the
|
drops oldest past `wxUndoMax`. The most recent `wxSaveMax`(40) frames are **persisted in the save
|
||||||
ring drops oldest past `wxUndoMax`. `S` in Live World aliases the forward step.
|
(v12)** so a load can rewind storms past the saved moment; a load also drops any stale pre-load
|
||||||
|
history. With no recorded past (e.g. immediately after a pre-v12 load) `,` rewinds the sky only and
|
||||||
|
says so. `S` in Live World aliases the forward step.
|
||||||
- Mouse hover (in either view) shows per-cell info. Clicking a tile opens a
|
- Mouse hover (in either view) shows per-cell info. Clicking a tile opens a
|
||||||
right-side detail panel: tile info header + the tile's subgrid drawn as a
|
right-side detail panel: tile info header + the tile's subgrid drawn as a
|
||||||
flat hoverable grid of subtiles (neighbor-owned subtiles dimmed). A high-res
|
flat hoverable grid of subtiles (neighbor-owned subtiles dimmed). A high-res
|
||||||
@ -610,11 +612,13 @@ key=value text block** instead of a raw POD dump, v7 appends the **biota populat
|
|||||||
block — three Organism lists per cell, gated by a flag byte, v8 appends the **Live World**
|
block — three Organism lists per cell, gated by a flag byte, v8 appends the **Live World**
|
||||||
clock — a flag byte + `liveTime`, v9 appends the **moons** block, v10 appends the **weather** block —
|
clock — a flag byte + `liveTime`, v9 appends the **moons** block, v10 appends the **weather** block —
|
||||||
humidity/cloud/rain, flag-gated, v11 also persists the **weather systems** + RNG so a load resumes
|
humidity/cloud/rain, flag-gated, v11 also persists the **weather systems** + RNG so a load resumes
|
||||||
active storms); newer-than-supported is
|
active storms, v12 appends the most recent **step-back frames** — `wxSaveMax`(40) weather snapshots
|
||||||
|
— so a load can rewind storms past the saved moment); newer-than-supported is
|
||||||
rejected. Older saves (no biota block) load fine with an empty population (press `L`);
|
rejected. Older saves (no biota block) load fine with an empty population (press `L`);
|
||||||
pre-v8 saves load with Live World off; pre-v9 saves synthesize moons from the seed; pre-v10
|
pre-v8 saves load with Live World off; pre-v9 saves synthesize moons from the seed; pre-v10
|
||||||
saves spin weather up live; pre-v11 saves load with no active storms (they respawn). `loadGame`
|
saves spin weather up live; pre-v11 saves load with no active storms (they respawn); pre-v12 saves
|
||||||
clears the step-back `wxUndo` history so a load can't restore stale pre-load weather.
|
load with no step-back history (you can still step forward then back). A load drops any **stale**
|
||||||
|
pre-load `wxUndo` history and reloads the saved one.
|
||||||
**As of v6, adding/removing PlanetConfig fields no longer breaks saves** — the saved
|
**As of v6, adding/removing PlanetConfig fields no longer breaks saves** — the saved
|
||||||
config is parsed like `planet.cfg` (unknown keys ignored, missing keys keep defaults),
|
config is parsed like `planet.cfg` (unknown keys ignored, missing keys keep defaults),
|
||||||
written at `precision(17)` so doubles round-trip exactly. (v6 cannot load pre-v6 saves —
|
written at `precision(17)` so doubles round-trip exactly. (v6 cannot load pre-v6 saves —
|
||||||
|
|||||||
@ -202,8 +202,10 @@ recurve (`weatherSystemSpeed`), **intensify** over warm sea / **decay+cull** ove
|
|||||||
behind them. Tropical systems past `weatherHurricaneStr` are hurricanes/typhoons; rendered as
|
behind them. Tropical systems past `weatherHurricaneStr` are hurricanes/typhoons; rendered as
|
||||||
animated cyclonic spiral markers (eye for cyclones) spinning by hemisphere, in 3D + 2D, under `K`.
|
animated cyclonic spiral markers (eye for cyclones) spinning by hemisphere, in 3D + 2D, under `K`.
|
||||||
The systems (+ their RNG/next-id) are **saved (v11)** alongside the humidity/cloud/rain fields, so a
|
The systems (+ their RNG/next-id) are **saved (v11)** alongside the humidity/cloud/rain fields, so a
|
||||||
load resumes active storms; `loadGame` clears the step-back `wxUndo` ring so a load can't restore a
|
load resumes active storms; a load also drops any stale pre-load `wxUndo` step-back ring and (v12)
|
||||||
stale pre-load weather state.
|
restores the most recent `wxSaveMax`(40) step-back frames from the file, so stepping back after a load
|
||||||
|
can rewind storms past the saved moment. Weather is integrated/path-dependent, so reversing it past
|
||||||
|
a save is only possible via this stored history — it can't be re-derived from the loaded moment.
|
||||||
|
|
||||||
## Live World viewer controls (follow-cam, 2D zoom, clock stepper)
|
## Live World viewer controls (follow-cam, 2D zoom, clock stepper)
|
||||||
|
|
||||||
|
|||||||
@ -262,6 +262,18 @@ void Viewer::saveGame(const char* path) {
|
|||||||
os.write(reinterpret_cast<const char*>(&lw), sizeof lw); // v8: Live World flag
|
os.write(reinterpret_cast<const char*>(&lw), sizeof lw); // v8: Live World flag
|
||||||
os.write(reinterpret_cast<const char*>(&liveTime), sizeof liveTime); // v8: live clock (hours)
|
os.write(reinterpret_cast<const char*>(&liveTime), sizeof liveTime); // v8: live clock (hours)
|
||||||
planet.writeState(os);
|
planet.writeState(os);
|
||||||
|
// v12: persist the most recent step-back frames so a load can rewind storms past the moment.
|
||||||
|
auto wD = [&](const std::vector<double>& v){ uint64_t m = v.size(); os.write((char*)&m, 8); if (m) os.write((const char*)v.data(), (std::streamsize)(m * sizeof(double))); };
|
||||||
|
uint32_t hn = (uint32_t)std::min<size_t>(wxUndo.size(), (size_t)wxSaveMax);
|
||||||
|
os.write((char*)&hn, 4);
|
||||||
|
for (size_t i = wxUndo.size() - hn; i < wxUndo.size(); ++i) {
|
||||||
|
const WxFrame& f = wxUndo[i];
|
||||||
|
os.write((char*)&f.t, 8);
|
||||||
|
wD(f.w.humidity); wD(f.w.cloud); wD(f.w.rain);
|
||||||
|
uint64_t sc = f.w.storms.size(); os.write((char*)&sc, 8);
|
||||||
|
if (sc) os.write((const char*)f.w.storms.data(), (std::streamsize)(sc * sizeof(WeatherSystem)));
|
||||||
|
os.write((char*)&f.w.rng, 4); os.write((char*)&f.w.nextId, 4);
|
||||||
|
}
|
||||||
setStatus(os ? std::string("Saved ") + path : "Save failed");
|
setStatus(os ? std::string("Saved ") + path : "Save failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +303,21 @@ void Viewer::loadGame(const char* path) {
|
|||||||
dtMy = settled ? planet.cflDtMy() : 0.0;
|
dtMy = settled ? planet.cflDtMy() : 0.0;
|
||||||
driftAccum = 0.0; formAccum = 0.0;
|
driftAccum = 0.0; formAccum = 0.0;
|
||||||
wxUndo.clear(); followId = 0; // drop stale step-back history / follow target
|
wxUndo.clear(); followId = 0; // drop stale step-back history / follow target
|
||||||
|
if (ver >= 12) { // v12: restore the saved step-back frames (rewind past load)
|
||||||
|
auto rD = [&](std::vector<double>& v){ uint64_t m = 0; is.read((char*)&m, 8);
|
||||||
|
if (!is || m > 4000000ull) { v.clear(); return; } v.resize((size_t)m);
|
||||||
|
if (m) is.read((char*)v.data(), (std::streamsize)(m * sizeof(double))); };
|
||||||
|
uint32_t hn = 0; is.read((char*)&hn, 4);
|
||||||
|
for (uint32_t k = 0; k < hn && is; ++k) {
|
||||||
|
WxFrame f; is.read((char*)&f.t, 8);
|
||||||
|
rD(f.w.humidity); rD(f.w.cloud); rD(f.w.rain);
|
||||||
|
uint64_t sc = 0; is.read((char*)&sc, 8); if (sc > 1000000ull) sc = 0;
|
||||||
|
f.w.storms.resize((size_t)sc);
|
||||||
|
if (sc) is.read((char*)f.w.storms.data(), (std::streamsize)(sc * sizeof(WeatherSystem)));
|
||||||
|
is.read((char*)&f.w.rng, 4); is.read((char*)&f.w.nextId, 4);
|
||||||
|
if (is) wxUndo.push_back(std::move(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
paused = true; selectedCell = -1; subgrids.clear();
|
paused = true; selectedCell = -1; subgrids.clear();
|
||||||
buildBorders(planet, borderR, borders, ridgeBorders);
|
buildBorders(planet, borderR, borders, ridgeBorders);
|
||||||
buildDriftArrows(planet, driftR, driftArrows, plateLabels);
|
buildDriftArrows(planet, driftR, driftArrows, plateLabels);
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
// ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing).
|
// ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing).
|
||||||
struct Viewer {
|
struct Viewer {
|
||||||
// ---- Files / save format ------------------------------------------------
|
// ---- Files / save format ------------------------------------------------
|
||||||
static constexpr uint32_t SAVE_VERSION = 11; // v11: +weather systems; v10: +weather fields; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3
|
static constexpr uint32_t SAVE_VERSION = 12; // v12: +step-back history; v11: +weather systems; v10: +weather fields; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3
|
||||||
|
static constexpr int wxSaveMax = 40; // most recent step-back frames persisted in a save
|
||||||
const char* CONFIG_PATH = "planet.cfg";
|
const char* CONFIG_PATH = "planet.cfg";
|
||||||
const char* SAVE_PATH = "planet.save";
|
const char* SAVE_PATH = "planet.save";
|
||||||
std::string configPath = "planet.cfg"; // initial config (--config overrides)
|
std::string configPath = "planet.cfg"; // initial config (--config overrides)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user