# Rewind gameplay (time.rewind) ## What it is for `time.rewind` keeps a bounded history of complete scene snapshots and lets the game go back a few seconds: objects that were created or destroyed since come back or disappear, parent links and references return, and so does the private state of the game. The kit only manages the history. A **scene provider** (another service of your game or engine adapter) owns the serialization of the scene and the atomic restore. Captures and restores run after physics, late updates and the event bus, never halfway through a tick. The kit draws nothing: a rewind effect or a counter is the interface's job. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "time.rewind" } gripforge_gamekit_configure { project_id: "gkp_…", id: "time.rewind", config: { "provider": "my.scene", "sampleHz": 20, "maxSeconds": 10 } } ``` Without a `provider` the kit records and restores nothing by itself: you pass snapshots explicitly and drive the transaction with your engine adapter (see "Service methods"). ## First example The provider service `capture`s the scene and `restore`s a plan. After the game changed objects and ran two ticks, asking the service `time.rewind` to go back further than the history (the request is clamped to the oldest frame) puts the scene back exactly as it was, then emits `rewind.completed`: ```json { "op": "call", "service": "time.rewind", "method": "rewind", "args": [999] } ``` A provider is a service with two methods: `capture()` returns a snapshot `{ objects: [...], globals? }` (each object has an `id`, and optionally a `generation`, a `parent`, `refs` and a `state` object), and `restore(plan)` applies the plan returned by the history and answers `true`. Any other answer, or an error, leaves the scene and the history unchanged. ## Settings and defaults | Setting | Type | Default | Meaning | |---|---|---|---| | `maxSeconds` | number | `10` | Longest history kept, in seconds (0.001 to 3600). Older frames are dropped on capture. | | `maxFrames` | integer | `300` | Most snapshots kept, whatever their age (1 to 10000). | | `maxBytes` | integer | `16777216` | Total bytes of encoded snapshots (1024 to 67108864). | | `maxFrameBytes` | integer | `1048576` | Bytes of one snapshot (256 to 4194304); must not exceed `maxBytes`. An oversized capture leaves the history intact. | | `maxObjects` | integer | `4096` | Objects in one snapshot (1 to 16384). | | `provider` | string | `""` | Service that provides `capture` and `restore`. Empty: explicit snapshots only. It cannot be `time.rewind`. | | `sampleHz` | number | `20` | Automatic captures per second when a provider is set (1 to 120), at most one per fixed tick. | ## Service methods Service `time.rewind`: - `rewind(seconds)` or `rewind({ preset })`: queue a restore through the provider. Answers `{ queued: true }`, or a refusal (see hooks). It fails when there is no provider, the history is empty or a restore is already pending. - `status()`: `frames`, `bytes`, `oldest`, `newest`, `availableSeconds`, `pending`, `branch`, `recording`, plus `queued` and `error`. - `recording(enabled)`: pause or resume the automatic capture. - `clear()`: empty the history. - `saveHistory()` and `loadHistory(save)`: export and import the history. - `capture(time, snapshot)`, `prepare(seconds, current)`, `commit(token)`, `cancel(token)`, `config()`, `plan()`: the explicit transaction for an engine adapter that has no provider. `prepare` returns a plan with a `token`; `commit` it once the engine applied the plan, `cancel` it otherwise. ## Verbs, events, hooks - `time_rewind_restore` (action): `{ preset }` or `{ seconds }`, the same as `rewind`. - `time_rewind_request` (action): `{ op, args }`, calls one of the methods above. - `time_rewind_available` (condition): a frame is held and no restore is pending. - `rewind.completed` (event): `{ time, seconds, clamped }`, after a successful restore. The abandoned future is discarded. - `rewind.failed` (event): `{ error }`, when a capture or a restore fails. A failed automatic capture stops the recording; a failed restore changes nothing. - `time.rewind.changed` (event): `{ op, result }`, after `recording`, `clear` or `loadHistory`. - `time.rewind.refused` (event): `{ op, by, reason }`, when a hook cancelled a request. - `time.rewind.restore` (hook): `{ seconds, preset }`; a tap can change `seconds` or cancel. A cancelled restore answers `{ ok: false, cancelled: true, by, reason }` and the history is unchanged. - `time.rewind.request` (hook): `{ op, args }` for `recording`, `clear` and `loadHistory`; cancellable. ## Hooks and modding The registry `time_rewind_presets` holds named rewinds, `{ id, seconds }` (0 to 3600), used by `rewind({ preset })` and the `time_rewind_restore` verb. A mod can add presets and tap the two hooks, for example to forbid a rewind during a boss fight. ## Engines The history is one Rust core shared by the native player, the engine adapters and the web (WebAssembly wrapper with the same decisions and verbs). The scene provider is what differs per engine: it captures and restores that engine's gameplay state. ## When it does not do what you expect - `rewind` fails with "rewind requires a scene provider": set `provider`, or use `prepare` and `commit` from your adapter. - `history is empty`: nothing was captured yet; the first capture happens when the game is ready. - Nothing is recorded: `recording` was paused, or an invalid snapshot stopped it after a `rewind.failed`; call `recording(true)`. - `rewind.failed` after a restore: the provider answered anything but `true`. It must leave the scene unchanged in that case. ## Limits No visual effect and no counter. It restores what the provider captured, nothing else. History taken in a scene is dropped when that scene is left. Only committed history is saved with the game; a restore in flight does not survive a reload. ## Versions Kit version 1.1.0 (named rewinds, hooks and verbs); 1.0.0 introduced the history, the two-phase restore and persistence.