diff --git a/eech-core-ts/README.md b/eech-core-ts/README.md index 8ef7bff74..308b735f4 100644 --- a/eech-core-ts/README.md +++ b/eech-core-ts/README.md @@ -6,14 +6,16 @@ TypeScript, compiled to **Lua 5.1** with The original C in `../aphavoc` and `../modules` is the behavioural authority. The campaign core runs without DCS World. The environment (physical positions, -replication, and later time, randomness and terrain) reaches it only through -narrow ports in `src/ports`. +replication, the frame clock, and later randomness and terrain) reaches it only +through narrow ports in `src/ports`. - `docs/architecture.md`: the bootstrap report (kernel boundary, ports, harness, coverage, Lua semantics, next slices). - `docs/port-manifest.md`: the authoritative C→TS mapping and status. - `docs/slices/assess-group-supplies.md`: slice 1, frozen, with its dependency trace and behaviour matrix. +- `docs/slices/group-update-timing.md`: slice 2, frozen, with its investigation, + boundary and behaviour matrix. ## Requirements @@ -42,16 +44,16 @@ npm run verify # every gate below, in order CI runs the same gates step by step in `.github/workflows/eech-core-ts.yml` for pull requests and pushes to `master` that touch `eech-core-ts/`, `aphavoc/` or `modules/`. -`npm run cref:record` re-records `test/scenarios/generated/c-reference-random.cases.ts` +`npm run cref:record` re-records the fixtures in `test/scenarios/generated/` from the executed C. `npm run gen:c` regenerates `src/generated` from the C sources. ## Layout ``` src/ - core/ ASSERT, C float semantics (toFloat32), maths from modules/maths + core/ ASSERT, C float semantics (toFloat32), maths, time (get_delta_time), configuration entity/system/ entity runtime: lists (with shared links), value function tables, messages, comms model - entity/special/ session, force, keysite, group, guide: the ported overloads and campaign functions + entity/special/ session, force, keysite, group, guide, update: the ported overloads, campaign functions and the update loop entity/mobile/ campaign-visible surface of aircraft and vehicles (position comes from a port) generated/ enums, database columns and constants generated from the EECH C (never hand-edited) ports/ what the campaign needs from the environment @@ -61,7 +63,7 @@ test/ unit/ vitest suites (JavaScript semantics, coverage) lua/ Lua 5.1 conformance runner and bundle smoke test c-reference/ C-vs-TS differential tests -c-reference/ extractor, shim and harness that execute the original C +c-reference/ extractor, harness environment and harness that execute the original C ``` ## Porting rules (short form) diff --git a/eech-core-ts/c-reference/README.md b/eech-core-ts/c-reference/README.md index 536324871..4b5ef4def 100644 --- a/eech-core-ts/c-reference/README.md +++ b/eech-core-ts/c-reference/README.md @@ -1,26 +1,43 @@ # C reference harness -This directory runs the **original** EECH functions against the same scenarios -as the TypeScript port. +This directory runs the **original** EECH C against the same scenarios as the +TypeScript port. -- `extract.mjs` copies the enums, structs, macros and functions listed in - `EXTRACTS` verbatim from `../aphavoc` and `../modules` into - `build/c-reference/eech_extracted.c`. Each block carries a `#line` directive to - its origin. The extractor fails if an original definition cannot be found. -- `eech_shim_prelude.h` and `eech_shim_types.h` supply the environment those - functions expect: `ASSERT` with debug-build meaning, entity records, lists - (including the shared `group_link`), and the accessor values of the scenario's - entities. The shim contains no campaign algorithm. -- `harness.c` reads a scenario on stdin (written by - `test/scenarios/campaign-scenario.ts :: serialiseScenario`), runs one operation - and prints the outcome. Floats are printed as bit patterns. -- `build.mjs` extracts and compiles with `-Wall -Werror`, and fails when no C - compiler is available. +- `extract.mjs` generates two files into `build/c-reference/`: + - `project.h`: `eech_harness_env.h`, verbatim fragments and **whole original + headers**, in the order listed in `PROJECT_H`; + - `eech_extracted.c`: verbatim functions from files too large to compile whole + (`EXTRACTED_C`). -The comparison covers the extracted functions. The accessor overloads -(`gp_int.c`, `ks_int.c`, ...) are shim-provided and are verified by source -reading. See `docs/architecture.md`, question 6. + Each fragment carries a `#line` directive to its origin. Extraction fails if + an original definition cannot be found. +- `REAL_TRANSLATION_UNITS` (in `extract.mjs`) lists the original files compiled + **unchanged** against that `project.h`, for example `gp_int.c`, `gp_float.c` + and `gp_updt.c`. +- `eech_harness_env.h` is the environment: the C library, the Windows SDK + `min`/`max`, `ASSERT` with debug-build meaning, debug output declarations, and + opaque engine types. `eech_harness_decls.h` declares the harness's own + globals and extracted functions. +- `harness.c` defines the dispatch tables and fills them through the original + `overload_*_functions ()`. It adds hand-written rows only for entity types + whose files are not compiled yet, and supplies the environment: frame delta, + comms, transport and mobile positions. It then reads a scenario on stdin and + prints the outcome, with floats as bit patterns. `stdout` is unbuffered. +- **Faults.** A `SIGSEGV` inside the NULL page is EECH's unguarded NULL + dereference. The handler reports `result null-dereference` and the final + state, then ends the process; it never resumes it. Any other fault kills the + process by signal, and the test driver treats that as a failed run, never as + an outcome (`test/c-reference/harness-faults.cref.test.ts`). +- **Signal safety is checked on the binary.** The handler's call graph may reach + no library call except `write` and `_exit`: fixed strings have compile-time + lengths, float bits are hex-encoded by hand, and `harness.c` is built with + `-fno-stack-protector`. `test/c-reference/harness-signal-safety.cref.test.ts` + walks the call graph in the compiled object with `objdump` and fails on + anything else, including calls the compiler inserts. +- `build.mjs` compiles everything. Our own files build with `-Werror`. Original + files keep their historical warnings, but mismatches with the environment are + errors (implicit declarations, pointer and int conversions). The build fails + when no C compiler is available. -The shim is meant to shrink over time. `docs/architecture.md`, "Shrinking the C -reference shim", lists the current shim entries, the order in which real EECH -translation units replace them, and the rules that stop the shim growing. +The remaining hand-written behaviour is listed, together with the plan to +retire it, in `docs/architecture.md`, "Shrinking the C reference shim". diff --git a/eech-core-ts/c-reference/build.mjs b/eech-core-ts/c-reference/build.mjs index 32f676712..212f7677a 100644 --- a/eech-core-ts/c-reference/build.mjs +++ b/eech-core-ts/c-reference/build.mjs @@ -1,50 +1,76 @@ #!/usr/bin/env node // -// Builds the C reference harness: extracts the original EECH functions and -// compiles them with the shim. Fails (exit 1) when no C compiler is available, -// so the C reference gate can never pass vacuously. +// Builds the C reference harness: generates project.h and the verbatim +// extracts, then compiles the harness, the extracts and the original EECH +// translation units listed in extract.mjs. Fails (exit 1) when no C compiler +// is available, so the C reference gate can never pass vacuously. // import { spawnSync } from "node:child_process"; -import { mkdirSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { generateExtracted } from "./extract.mjs"; +import { AP_SOURCE, REAL_TRANSLATION_UNITS, writeGenerated } from "./extract.mjs"; const here = dirname(fileURLToPath(import.meta.url)); const projectRoot = join(here, ".."); +const repoRoot = join(projectRoot, ".."); const outDir = join(projectRoot, "build", "c-reference"); export const HARNESS_BINARY = join(outDir, "harness"); -export function buildHarness() { - mkdirSync(outDir, { recursive: true }); - writeFileSync(join(outDir, "eech_extracted.c"), generateExtracted()); +export const HARNESS_BUILD_DIR = outDir; - const cc = process.env.CC || "cc"; - const args = [ - "-std=c99", - "-O0", - "-Wall", - "-Werror", - "-Wno-unused-function", - "-Wno-unused-variable", - "-Wno-unused-but-set-variable", - "-ffp-contract=off", - "-fno-fast-math", - "-I", - outDir, - "-I", - here, - join(here, "harness.c"), - "-lm", - "-o", - HARNESS_BINARY, - ]; +const COMMON = [ + "-std=gnu99", + "-O0", + "-ffp-contract=off", + "-fno-fast-math", + // build/c-reference first, so the original files' #include "project.h" finds the reduced one + "-I", + outDir, + "-I", + here, + "-I", + join(repoRoot, AP_SOURCE), +]; + +// Our own sources: every warning is an error. +// -fno-stack-protector: the SIGSEGV handler's call graph must reach no library +// call but write () and _exit (), and the stack protector would add +// __stack_chk_fail (checked by test/c-reference/harness-signal-safety.cref.test.ts). +const OWN_FLAGS = ["-Wall", "-Werror", "-Wno-unused-function", "-Wno-unused-variable", "-Wno-unused-but-set-variable", "-fno-stack-protector"]; + +export const HARNESS_OBJECT_NAME = "harness.c.o"; + +// Original EECH sources: their historical warnings are not ours to fix, but the +// ones that would hide a mismatch with the harness environment are errors. +const ORIGINAL_FLAGS = ["-Werror=implicit-function-declaration", "-Werror=incompatible-pointer-types", "-Werror=int-conversion", "-Werror=return-type", "-w"]; + +function compile(cc, source, flags, objectName = `${source.replace(/[\\/]/g, "_")}.o`) { + const object = join(outDir, objectName); + const args = [...COMMON, ...flags, "-c", source, "-o", object]; const result = spawnSync(cc, args, { encoding: "utf8" }); if (result.error || result.status !== 0) { throw new Error(`C reference harness build failed (${cc} ${args.join(" ")}):\n${result.error ?? ""}${result.stdout}${result.stderr}`); } + return object; +} + +export function buildHarness() { + writeGenerated(outDir); + + const cc = process.env.CC || "cc"; + const objects = [ + compile(cc, join(here, "harness.c"), OWN_FLAGS, HARNESS_OBJECT_NAME), + // verbatim original code: original-code flags + compile(cc, join(outDir, "eech_extracted.c"), ORIGINAL_FLAGS), + ...REAL_TRANSLATION_UNITS.map((unit) => compile(cc, join(repoRoot, unit), ORIGINAL_FLAGS)), + ]; + + const link = spawnSync(cc, [...objects, "-lm", "-o", HARNESS_BINARY], { encoding: "utf8" }); + if (link.error || link.status !== 0) { + throw new Error(`C reference harness link failed:\n${link.error ?? ""}${link.stdout}${link.stderr}`); + } return HARNESS_BINARY; } diff --git a/eech-core-ts/c-reference/eech_harness_decls.h b/eech-core-ts/c-reference/eech_harness_decls.h new file mode 100644 index 000000000..2c62c1a3f --- /dev/null +++ b/eech-core-ts/c-reference/eech_harness_decls.h @@ -0,0 +1,29 @@ +/* + * Declarations the harness adds after the original headers. + */ + +#ifndef EECH_HARNESS_DECLS_H +#define EECH_HARNESS_DECLS_H + +/* cmndline.h (the EECH.INI "entity update frame rate" setting) */ +extern int + command_line_entity_update_frame_rate; + +/* functions extracted into eech_extracted.c and called from harness.c */ +extern void harness_overload_group_link_parent_responses (void); + +extern void harness_default_update_link_responses (void); + +extern void (*harness_default_set_entity_int_value) (entity *en, int_types type, int value); + +extern void (*harness_default_set_entity_float_value) (entity *en, float_types type, float value); + +extern void update_client_server_entities (void); + +extern float get_2d_range (const vec3d *v1, const vec3d *v2); + +extern float get_approx_2d_range (const vec3d *v1, const vec3d *v2); + +extern void assess_group_supplies (entity *en); + +#endif diff --git a/eech-core-ts/c-reference/eech_harness_env.h b/eech-core-ts/c-reference/eech_harness_env.h new file mode 100644 index 000000000..a427475b4 --- /dev/null +++ b/eech-core-ts/c-reference/eech_harness_env.h @@ -0,0 +1,61 @@ +/* + * Harness environment. + * + * What the platform, the engine modules (modules/system, modules/maths) and + * the network layer supply to the original EECH translation units. It holds + * no campaign behaviour. The generated build/c-reference/project.h includes + * this first, then verbatim fragments and whole original headers (see + * c-reference/extract.mjs). + * + * Every definition here is either: + * - platform: the C library, and the Windows SDK's min/max macros; + * - engine: types that included headers only name in prototypes + * (matrix3x3, viewpoint), and the debug output functions; + * - debug-build validation that is not ported. + */ + +#ifndef EECH_HARNESS_ENV_H +#define EECH_HARNESS_ENV_H + +#include +#include +#include +#include +#include +#include + +/* the TypeScript port models C float arithmetic as IEEE single precision + evaluated at declared type; the reference must do the same */ +#if !defined (FLT_EVAL_METHOD) || FLT_EVAL_METHOD != 0 +#error "the C reference harness requires FLT_EVAL_METHOD == 0" +#endif + +#define TRUE 1 +#define FALSE 0 + +/* Windows SDK windef.h: EECH relies on these platform macros (e.g. gp_updt.c + max (raw->sleep, 0.0f)). Note max (NaN, 0.0f) is 0.0f, unlike fmax. */ +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* modules/system/assert.h with EECH's debug-build meaning: the check is + performed and a failure is reported to the harness. */ +void harness_assert (const char *expression); + +#define ASSERT(E) if (!(E)) (harness_assert (#E)) +#define debug_assert(E) if (!(E)) (harness_assert (#E)) + +/* modules/system/debug.h */ +extern void debug_fatal (const char *string, ...); +extern void debug_log (const char *string, ...); + +/* engine types that included headers only name in prototypes */ +typedef float matrix3x3[3][3]; +typedef struct VIEWPOINT viewpoint; + +/* en_debug/en_valid.h: debug-build checks of the comms dispatch context, + excluded from the port (docs/port-manifest.md) */ +#define validate_client_server_local_fn() +#define validate_client_server_remote_fn() + +#endif diff --git a/eech-core-ts/c-reference/eech_shim_prelude.h b/eech-core-ts/c-reference/eech_shim_prelude.h deleted file mode 100644 index 7bd694fca..000000000 --- a/eech-core-ts/c-reference/eech_shim_prelude.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Harness prelude: the parts of EECH's global environment (project.h, - * modules/system/assert.h) that the extracted functions expect to exist. - * - * ASSERT / debug_assert keep EECH's debug-build meaning (the check is - * performed); a failed check is reported to the harness instead of aborting. - */ - -#ifndef EECH_SHIM_PRELUDE_H -#define EECH_SHIM_PRELUDE_H - -#include -#include -#include -#include - -/* the TypeScript port models C float arithmetic as IEEE single precision - evaluated at declared type; the reference must do the same */ -#if !defined (FLT_EVAL_METHOD) || FLT_EVAL_METHOD != 0 -#error "the C reference harness requires FLT_EVAL_METHOD == 0" -#endif - -#define TRUE 1 -#define FALSE 0 - -#define DEBUG_MODULE 0 -#define LANDING_DEBUG 0 - -void harness_assert (const char *expression); - -#define ASSERT(E) if (!(E)) (harness_assert (#E)) -#define debug_assert(E) if (!(E)) (harness_assert (#E)) - -#endif diff --git a/eech-core-ts/c-reference/eech_shim_types.h b/eech-core-ts/c-reference/eech_shim_types.h deleted file mode 100644 index fc1507ccb..000000000 --- a/eech-core-ts/c-reference/eech_shim_types.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Harness shim: the entity runtime environment of the extracted EECH - * functions. - * - * This is NOT a port of the entity system. It supplies, for the scenario's - * entities only, the accessor behaviour the ported TypeScript entity overloads - * also implement (gp_int.c, ks_int.c, gp_vec3d.c, ...). The C reference - * comparison therefore verifies the extracted functions (group.c, - * keysite.c, force.c, range.c, en_msgs.c), not these accessors; the accessors - * are verified by source reading (see docs/slices/assess-group-supplies.md). - */ - -#ifndef EECH_SHIM_TYPES_H -#define EECH_SHIM_TYPES_H - -typedef enum ENTITY_TYPES entity_types; -typedef enum ENTITY_SIDES entity_sides; -typedef enum LIST_TYPES list_types; -typedef enum INT_TYPES int_types; -typedef enum FLOAT_TYPES float_types; -typedef enum VEC3D_TYPES vec3d_types; -typedef enum PTR_TYPES ptr_types; -typedef enum ENTITY_MESSAGES entity_messages; -typedef int entity_sub_types; -typedef struct SUPPLY_TYPE supply_type; -typedef struct VEC3D vec3d; - -typedef struct ENTITY -{ - entity_types - type; - - int - index; - - void - *data; - - struct ENTITY - *first_child[NUM_LIST_TYPES], - *parent[NUM_LIST_TYPES], - *child_succ[NUM_LIST_TYPES], - *child_pred[NUM_LIST_TYPES]; -} entity; - -/* raw structs: only the members the extracted code and the shim use */ - -typedef struct -{ - entity_sides - side; - - supply_type - supplies; - - int - resupply_source; /* group_database [sub_type].resupply_source, supplied by the scenario */ -} group; - -typedef struct -{ - entity_sub_types - sub_type; - - entity_sides - side; - - int - in_use; - - vec3d - position; - - supply_type - supplies; -} keysite; - -typedef struct -{ - entity_sides - side; -} force; - -typedef struct -{ - vec3d - position; -} mobile; - -extern entity - *session_entity; - -#define get_session_entity() (session_entity) - -extern int (*message_responses[NUM_ENTITY_TYPES][NUM_ENTITY_MESSAGES]) (entity_messages message, entity *receiver, entity *sender, va_list pargs); - -void *get_local_entity_data (entity *en); -entity_types get_local_entity_type (entity *en); -entity *get_local_entity_first_child (entity *en, list_types type); -entity *get_local_entity_parent (entity *en, list_types type); -entity *get_local_entity_child_succ (entity *en, list_types type); -int get_local_entity_int_value (entity *en, int_types type); -float get_local_entity_float_value (entity *en, float_types type); -void set_client_server_entity_float_value (entity *en, float_types type, float value); -vec3d *get_local_entity_vec3d_ptr (entity *en, vec3d_types type); - -int notify_local_entity (entity_messages message, entity *receiver, entity *sender, ...); -entity *get_local_force_entity (entity_sides side); -entity *get_closest_keysite (entity_sub_types type, entity_sides side, vec3d *pos, float min_range, float *actual_range, int outside_of_range, entity *exclude_keysite); -float get_2d_range (const vec3d *v1, const vec3d *v2); -float get_approx_2d_range (const vec3d *v1, const vec3d *v2); -void assess_group_supplies (entity *en); - -#endif diff --git a/eech-core-ts/c-reference/extract.mjs b/eech-core-ts/c-reference/extract.mjs index 68b72efaa..33e0f8b85 100644 --- a/eech-core-ts/c-reference/extract.mjs +++ b/eech-core-ts/c-reference/extract.mjs @@ -1,10 +1,21 @@ #!/usr/bin/env node // -// Copies original EECH C definitions verbatim into build/c-reference/eech_extracted.c -// so the harness executes the original code, not a re-implementation. +// Generates the C reference harness sources from the original EECH C: // -// Each extracted block is preceded by a #line directive, so compiler -// diagnostics and debuggers point at the original file and line. +// build/c-reference/project.h the reduced project.h that the original +// translation units include: the harness +// environment, verbatim fragments and +// WHOLE original headers +// build/c-reference/eech_extracted.c verbatim copies of original functions and +// definitions that live in files too large +// to compile whole +// +// Original translation units that are compiled unchanged are listed in +// REAL_TRANSLATION_UNITS; build.mjs compiles them against the generated +// project.h. +// +// Each fragment is preceded by a #line directive to its origin. Extraction +// fails loudly if an original definition cannot be found. // import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; @@ -15,35 +26,96 @@ const here = dirname(fileURLToPath(import.meta.url)); const projectRoot = join(here, ".."); const repoRoot = join(projectRoot, ".."); -// kind: "function" (signature line through matching brace), "enum" (enum TAG { ... };), -// "struct" (struct TAG { ... };), "define" (single #define line) -export const EXTRACTS = [ - { kind: "enum", name: "ENTITY_TYPES", file: "aphavoc/source/entity/system/en_types/en_types.h" }, - { kind: "enum", name: "ENTITY_SIDES", file: "aphavoc/source/entity/system/en_types/en_side.h" }, - { kind: "enum", name: "LIST_TYPES", file: "aphavoc/source/entity/system/en_funcs/en_list.h" }, - { kind: "enum", name: "INT_TYPES", file: "aphavoc/source/entity/system/en_funcs/en_int.h" }, - { kind: "enum", name: "FLOAT_TYPES", file: "aphavoc/source/entity/system/en_funcs/en_float.h" }, - { kind: "enum", name: "VEC3D_TYPES", file: "aphavoc/source/entity/system/en_funcs/en_vec3d.h" }, - { kind: "enum", name: "PTR_TYPES", file: "aphavoc/source/entity/system/en_funcs/en_ptr.h" }, - { kind: "enum", name: "ENTITY_MESSAGES", file: "aphavoc/source/entity/system/en_msgs/en_msgs.h" }, - { kind: "enum", name: "ENTITY_SUB_TYPE_CARGO", file: "aphavoc/source/entity/system/en_types/en_sbtyp.h" }, - { kind: "enum", name: "ENTITY_SUB_TYPE_GROUPS", file: "aphavoc/source/entity/system/en_types/en_sbtyp.h" }, - { kind: "enum", name: "ENTITY_SUB_TYPE_KEYSITES", file: "aphavoc/source/entity/system/en_types/en_sbtyp.h" }, - { kind: "enum", name: "GROUP_MODE_TYPES", file: "aphavoc/source/ai_extrn.h" }, - { kind: "enum", name: "COMMS_MODEL_TYPES", file: "aphavoc/source/comms/comms.h" }, - { kind: "enum", name: "RESUPPLY_SOURCE_TYPE", file: "aphavoc/source/entity/system/en_types/en_suply.h" }, - { kind: "struct", name: "SUPPLY_TYPE", file: "aphavoc/source/entity/system/en_types/en_suply.h" }, +export const AP_SOURCE = "aphavoc/source"; + +// Original EECH translation units compiled unchanged into the harness. +export const REAL_TRANSLATION_UNITS = [ + "aphavoc/source/entity/special/group/gp_dbase.c", + "aphavoc/source/entity/special/group/gp_float.c", + "aphavoc/source/entity/special/group/gp_int.c", + "aphavoc/source/entity/special/group/gp_list.c", + "aphavoc/source/entity/special/group/gp_ptr.c", + "aphavoc/source/entity/special/group/gp_updt.c", + "aphavoc/source/entity/special/group/gp_vec3d.c", + "aphavoc/source/entity/special/update/up_list.c", + "aphavoc/source/entity/special/update/up_msgs.c", +]; + +// Contents of the generated project.h, in order. +// kind "include": a whole original header (path relative to aphavoc/source) +export const PROJECT_H = [ + { kind: "local", name: "eech_harness_env.h" }, { kind: "struct", name: "VEC3D", file: "modules/maths/vector.h" }, + { kind: "regex", pattern: "\\ntypedef struct VEC3D vec3d;", file: "modules/maths/vector.h" }, { kind: "define", name: "bound", file: "modules/maths/miscmath.h" }, { kind: "define", name: "METRE", file: "modules/maths/constant.h" }, { kind: "define", name: "KILOMETRE", file: "modules/maths/constant.h" }, - { kind: "define", name: "FUEL_USAGE_ACCELERATOR", file: "aphavoc/source/entity/system/en_types/en_suply.h" }, - { kind: "define", name: "AMMO_USAGE_ACCELERATOR", file: "aphavoc/source/entity/system/en_types/en_suply.h" }, - { kind: "define", name: "DEBUG_SUPPLY", file: "aphavoc/source/entity/system/en_types/en_suply.h" }, - { kind: "shim", name: "eech_shim_types" }, + { kind: "regex", pattern: "\\nextern float\\n\\tsystem_delta_time,\\n\\tsystem_one_over_delta_time;", file: "modules/system/time.h" }, + { kind: "regex", pattern: "\\nextern int\\n\\tlocked_frame_rate;", file: "modules/system/time.h" }, + { kind: "regex", pattern: "\\nextern void set_manual_delta_time \\(float delta_time\\);", file: "modules/system/time.h" }, + { kind: "define", name: "get_delta_time", file: "modules/system/time.h" }, + { kind: "enum", name: "COMMS_MODEL_TYPES", file: "aphavoc/source/comms/comms.h" }, + { kind: "regex", pattern: "\\ntypedef enum COMMS_MODEL_TYPES comms_model_types;", file: "aphavoc/source/comms/comms.h" }, + { kind: "enum", name: "COMMS_DATA_FLOW_TYPES", file: "aphavoc/source/comms/comms.h" }, + { kind: "regex", pattern: "\\ntypedef enum COMMS_DATA_FLOW_TYPES comms_data_flow_types;", file: "aphavoc/source/comms/comms.h" }, + { kind: "regex", pattern: "\\nextern comms_model_types\\n\\s*system_comms_model;", file: "aphavoc/source/comms/comms.h" }, + { kind: "regex", pattern: "\\nextern comms_data_flow_types\\n\\s*system_comms_data_flow;", file: "aphavoc/source/comms/comms.h" }, + { kind: "define", name: "get_comms_model", file: "aphavoc/source/comms/comms.h" }, + { kind: "define", name: "get_comms_data_flow", file: "aphavoc/source/comms/comms.h" }, + { kind: "enum", name: "MAP_LAYER_CONTROL_TYPES", file: "aphavoc/source/ui_menu/ingame/common/map.h" }, + { kind: "define", name: "MAP_LAYER_CONTROL_NONE", file: "aphavoc/source/ui_menu/ingame/common/map.h" }, + { kind: "enum", name: "MAP_ICON_TYPE", file: "aphavoc/source/ui_menu/ingame/common/map.h" }, + { kind: "include", name: "entity/system/en_types/en_types.h" }, + { kind: "include", name: "entity/system/en_main/en_heap.h" }, + { kind: "include", name: "entity/system/en_msgs/en_msgs.h" }, + { kind: "include", name: "entity/system/en_funcs/en_funcs.h" }, + { kind: "include", name: "entity/system/en_comms/en_comms.h" }, + { kind: "include", name: "entity/system/en_debug/en_dbmsg.h" }, + { kind: "include", name: "ai_extrn.h" }, + { kind: "include", name: "entity/special/update/up_update.h" }, + { kind: "include", name: "entity/special/division/division.h" }, + { kind: "include", name: "entity/special/group/group.h" }, + { kind: "include", name: "entity/tacview/tacview.h" }, + { kind: "regex", pattern: "\\nextern entity\\n\\t\\*session_entity;", file: "aphavoc/source/entity/special/session/session.h" }, + { kind: "define", name: "get_session_entity", file: "aphavoc/source/entity/special/session/session.h" }, + { kind: "prototype", name: "get_local_force_entity", file: "aphavoc/source/entity/special/force/force.h" }, + { kind: "prototype", name: "add_group_type_to_force_info", file: "aphavoc/source/entity/special/force/force.h" }, + { kind: "prototype", name: "remove_group_type_from_force_info", file: "aphavoc/source/entity/special/force/force.h" }, + { kind: "prototype", name: "get_closest_keysite", file: "aphavoc/source/entity/special/keysite/keysite.h" }, + { kind: "local", name: "eech_harness_decls.h" }, +]; + +// Contents of the generated eech_extracted.c, in order. +export const EXTRACTED_C = [ + { kind: "raw", text: '#include "project.h"\n\n#define DEBUG_MODULE 0\n\n#define LANDING_DEBUG 0\n' }, + + // modules/system/time.c: the delta time the entity update loop reads and overrides + { kind: "define", name: "DELTA_TIME_HISTORY_SIZE", file: "modules/system/time.c" }, + { kind: "regex", pattern: "\\nfloat\\n\\tsystem_delta_time_average = 0\\.1,\\n\\tsystem_delta_time = 0\\.1,\\n\\tsystem_one_over_delta_time = 10\\.0;", file: "modules/system/time.c" }, + { kind: "regex", pattern: "\\nint\\n\\tlocked_frame_rate = FALSE;", file: "modules/system/time.c" }, + { kind: "regex", pattern: "\\nstatic float\\n\\tsystem_delta_time_history\\[DELTA_TIME_HISTORY_SIZE\\];", file: "modules/system/time.c" }, + { kind: "regex", pattern: "\\nstatic int\\n\\tsystem_delta_time_history_position;", file: "modules/system/time.c" }, + { kind: "function", name: "set_manual_delta_time", signature: "void set_manual_delta_time (float delta_time)", file: "modules/system/time.c" }, + + // modules/maths/range.c { kind: "function", name: "get_2d_range", signature: "float get_2d_range (const vec3d *v1, const vec3d *v2)", file: "modules/maths/range.c" }, { kind: "function", name: "get_approx_2d_range", signature: "float get_approx_2d_range (const vec3d *v1, const vec3d *v2)", file: "modules/maths/range.c" }, + + // entity system defaults and runtime + { kind: "function", name: "default_set_entity_int_value", signature: "static void default_set_entity_int_value (entity *en, int_types type, int value)", file: "aphavoc/source/entity/system/en_funcs/en_int.c" }, + { kind: "function", name: "default_set_entity_float_value", signature: "static void default_set_entity_float_value (entity *en, float_types type, float value)", file: "aphavoc/source/entity/system/en_funcs/en_float.c" }, { kind: "function", name: "notify_local_entity", signature: "int notify_local_entity (entity_messages message, entity *receiver, entity *sender, ...)", file: "aphavoc/source/entity/system/en_msgs/en_msgs.c" }, + { kind: "function", name: "default_message_response", signature: "static int default_message_response (entity_messages message, entity *receiver, entity *sender, va_list pargs)", file: "aphavoc/source/entity/system/en_msgs/en_msgs.c" }, + { kind: "function", name: "insert_local_entity_into_parents_child_list", signature: "void insert_local_entity_into_parents_child_list (entity *en, list_types type, entity *parent, entity *pred)", file: "aphavoc/source/entity/system/en_funcs/en_list.c" }, + { kind: "function", name: "delete_local_entity_from_parents_child_list", signature: "void delete_local_entity_from_parents_child_list (entity *en, list_types type)", file: "aphavoc/source/entity/system/en_funcs/en_list.c" }, + + // entity/special/update/up_update.c: the entity update loop + { kind: "regex", pattern: "\\nentity\\n\\t\\*update_entity = NULL,\\n\\t\\*update_succ = NULL;", file: "aphavoc/source/entity/special/update/up_update.c" }, + { kind: "regex", pattern: "\\nunsigned int\\n\\tmoved_entities[^;]*;", file: "aphavoc/source/entity/special/update/up_update.c" }, + { kind: "function", name: "update_client_server_entities", signature: "void update_client_server_entities (void)", file: "aphavoc/source/entity/special/update/up_update.c" }, + { kind: "function", name: "set_entity_update_frame_rate", signature: "int set_entity_update_frame_rate (int frame_rate)", file: "aphavoc/source/entity/special/update/up_update.c" }, + + // campaign functions (slice 1) { kind: "function", name: "get_local_force_entity", signature: "entity *get_local_force_entity (entity_sides side)", file: "aphavoc/source/entity/special/force/force.c" }, { kind: "function", @@ -52,6 +124,23 @@ export const EXTRACTS = [ file: "aphavoc/source/entity/special/keysite/keysite.c", }, { kind: "function", name: "assess_group_supplies", signature: "void assess_group_supplies (entity *en)", file: "aphavoc/source/entity/special/group/group.c" }, + + // group link/unlink parent responses (gp_msgs.c is otherwise not compiled) + { kind: "function", name: "response_to_link_parent", signature: "static int response_to_link_parent (entity_messages message, entity *receiver, entity *sender, va_list pargs)", file: "aphavoc/source/entity/special/group/gp_msgs.c" }, + { kind: "function", name: "response_to_unlink_parent", signature: "static int response_to_unlink_parent (entity_messages message, entity *receiver, entity *sender, va_list pargs)", file: "aphavoc/source/entity/special/group/gp_msgs.c" }, + { + kind: "wrap", + prologue: "void harness_overload_group_link_parent_responses (void)\n{", + epilogue: "}", + parts: [ + { kind: "regex", pattern: "\\n\\tmessage_responses\\[ENTITY_TYPE_GROUP\\]\\[ENTITY_MESSAGE_LINK_PARENT\\][^;]*;", file: "aphavoc/source/entity/special/group/gp_msgs.c" }, + { kind: "regex", pattern: "\\n\\tmessage_responses\\[ENTITY_TYPE_GROUP\\]\\[ENTITY_MESSAGE_UNLINK_PARENT\\][^;]*;", file: "aphavoc/source/entity/special/group/gp_msgs.c" }, + ], + }, + { + kind: "raw", + text: "/* C defaults for the update entity: its link responses are only overloaded under DEBUG_MODULE (up_msgs.c) */\nvoid harness_default_update_link_responses (void)\n{\n\tmessage_responses[ENTITY_TYPE_UPDATE][ENTITY_MESSAGE_LINK_CHILD] = default_message_response;\n\tmessage_responses[ENTITY_TYPE_UPDATE][ENTITY_MESSAGE_LINK_PARENT] = default_message_response;\n\tmessage_responses[ENTITY_TYPE_UPDATE][ENTITY_MESSAGE_UNLINK_PARENT] = default_message_response;\n}\n\nvoid (*harness_default_set_entity_int_value) (entity *en, int_types type, int value) = default_set_entity_int_value;\n\nvoid (*harness_default_set_entity_float_value) (entity *en, float_types type, float value) = default_set_entity_float_value;\n", + }, ]; function lineOf(text, offset) { @@ -74,9 +163,24 @@ function extractBraced(text, start, what) { throw new Error(`unbalanced braces extracting ${what}`); } +function readSource(file) { + return readFileSync(join(repoRoot, file), "latin1").replace(/\r\n/g, "\n"); +} + export function extractOne(spec) { - const path = join(repoRoot, spec.file); - const text = readFileSync(path, "latin1").replace(/\r\n/g, "\n"); + if (spec.kind === "local") { + return `#include "${spec.name}"\n`; + } + if (spec.kind === "include") { + return `#include "${spec.name}"\n`; + } + if (spec.kind === "raw") { + return `${spec.text}\n`; + } + if (spec.kind === "wrap") { + return `${spec.prologue}\n${spec.parts.map(extractOne).join("")}${spec.epilogue}\n`; + } + const text = readSource(spec.file); let start; let end; if (spec.kind === "function") { @@ -97,37 +201,52 @@ export function extractOne(spec) { throw new Error(`${spec.kind} ${spec.name} not terminated by ;`); } end += 1; - } else { + } else if (spec.kind === "define") { const match = new RegExp(`\\n#define ${spec.name}\\b[^\\n]*`).exec(text); if (!match) { throw new Error(`#define ${spec.name} not found in ${spec.file}`); } start = match.index + 1; end = match.index + match[0].length; + } else if (spec.kind === "prototype") { + const match = new RegExp(`\\nextern [^\\n(]*\\b${spec.name} \\([^\\n]*\\);`).exec(text); + if (!match) { + throw new Error(`prototype ${spec.name} not found in ${spec.file}`); + } + start = match.index + 1; + end = match.index + match[0].length; + } else if (spec.kind === "regex") { + const match = new RegExp(spec.pattern).exec(text); + if (!match) { + throw new Error(`/${spec.pattern}/ not found in ${spec.file}`); + } + start = match.index + (match[0].startsWith("\n") ? 1 : 0); + end = match.index + match[0].length; + } else { + throw new Error(`unknown extract kind ${spec.kind}`); } return `#line ${lineOf(text, start)} "${spec.file}"\n${text.slice(start, end)}\n`; } +const BANNER = "/* GENERATED by c-reference/extract.mjs - verbatim original EECH C. Do not edit. */\n"; + +export function generateProjectH() { + return [BANNER, "#ifndef EECH_HARNESS_PROJECT_H", "#define EECH_HARNESS_PROJECT_H", "", ...PROJECT_H.map(extractOne), "#endif", ""].join("\n"); +} + export function generateExtracted() { - const parts = [ - "/* GENERATED by c-reference/extract.mjs - verbatim copies of original EECH C. Do not edit. */", - "", - ]; - for (const spec of EXTRACTS) { - if (spec.kind === "shim") { - parts.push(`#include "${spec.name}.h"`, ""); - } else { - parts.push(extractOne(spec)); - } - } - return parts.join("\n"); + return [BANNER, ...EXTRACTED_C.map(extractOne)].join("\n"); +} + +export function writeGenerated(outDir) { + mkdirSync(outDir, { recursive: true }); + writeFileSync(join(outDir, "project.h"), generateProjectH()); + writeFileSync(join(outDir, "eech_extracted.c"), generateExtracted()); } const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]; if (isMain) { - const outDir = join(projectRoot, "build", "c-reference"); - mkdirSync(outDir, { recursive: true }); - writeFileSync(join(outDir, "eech_extracted.c"), generateExtracted()); - console.log("wrote build/c-reference/eech_extracted.c"); + writeGenerated(join(projectRoot, "build", "c-reference")); + console.log("wrote build/c-reference/project.h and eech_extracted.c"); } diff --git a/eech-core-ts/c-reference/harness.c b/eech-core-ts/c-reference/harness.c index 017d81d79..76faae747 100644 --- a/eech-core-ts/c-reference/harness.c +++ b/eech-core-ts/c-reference/harness.c @@ -1,34 +1,126 @@ /* * EECH C reference harness. * - * Reads one scenario (the line format written by test/scenarios/campaign-scenario.ts :: - * serialiseScenario) from stdin, builds the entity state, runs ONE operation - * through the original EECH functions (extracted verbatim into - * build/c-reference/eech_extracted.c) and prints the observable outcome. + * Reads one scenario on stdin, builds the entity state, runs the requested + * operation through the ORIGINAL EECH code and prints the observable outcome. + * Original code reaches the harness three ways: + * + * - whole translation units compiled unchanged (gp_int.c, gp_float.c, + * gp_list.c, gp_vec3d.c, gp_ptr.c, gp_updt.c, gp_dbase.c, up_list.c, + * up_msgs.c; see REAL_TRANSLATION_UNITS in extract.mjs); + * - verbatim function extracts (build/c-reference/eech_extracted.c); + * - whole original headers (build/c-reference/project.h). + * + * The harness supplies only: + * - the dispatch tables and their fail-loud defaults, filled by the + * ORIGINAL overload_*_functions () where those files are compiled; + * - hand-written rows for entity types whose files are not compiled yet + * (session, force, keysite, guide, helicopter); every such row is listed + * in docs/architecture.md, "Shrinking the C reference shim"; + * - the environment: frame delta time (set_delta_time), comms model, + * transport (transmit_entity_comms_message), mobile positions, and debug + * output. + * + * Input: see test/scenarios/campaign-scenario.ts :: serialiseScenario and + * test/scenarios/update-timeline.ts :: serialiseTimeline. * * Output lines (floats as IEEE 754 single precision bit patterns, %08x): * - * transmit set_client_server_entity_float_value (server) + * transmit ENTITY_COMMS_FLOAT_VALUE sent by a server setter * message delivery to FORCE/LOW_ON_SUPPLIES response * closest result of op closest * range + * step + * timer one line per timeline group after each step * result ok | result assert | result null-dereference + * + * A NULL dereference (a fault inside the NULL page) is an EECH outcome and is + * reported. Any other fatal signal is a harness or original-code defect: the + * process dies by that signal and the caller treats the run as failed. * final group * final keysite one line per keysite, scenario order */ #include -#include -#include - -#include "eech_shim_prelude.h" +#include +#include +#include +#include + +#include "project.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// dispatch tables (C: en_int.c, en_float.c, en_vec3d.c, en_ptr.c, en_list.c, en_updt.c, en_msgs.c) +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void (*fn_set_local_entity_raw_int_value[NUM_ENTITY_TYPES][NUM_INT_TYPES]) (entity *en, int_types type, int value); +void (*fn_set_local_entity_int_value[NUM_ENTITY_TYPES][NUM_INT_TYPES]) (entity *en, int_types type, int value); +void (*fn_set_client_server_entity_int_value[NUM_ENTITY_TYPES][NUM_INT_TYPES][NUM_COMMS_MODEL_TYPES]) (entity *en, int_types type, int value); +int (*fn_get_local_entity_int_value[NUM_ENTITY_TYPES][NUM_INT_TYPES]) (entity *en, int_types type); + +void (*fn_set_local_entity_raw_float_value[NUM_ENTITY_TYPES][NUM_FLOAT_TYPES]) (entity *en, float_types type, float value); +void (*fn_set_local_entity_float_value[NUM_ENTITY_TYPES][NUM_FLOAT_TYPES]) (entity *en, float_types type, float value); +void (*fn_set_client_server_entity_float_value[NUM_ENTITY_TYPES][NUM_FLOAT_TYPES][NUM_COMMS_MODEL_TYPES]) (entity *en, float_types type, float value); +float (*fn_get_local_entity_float_value[NUM_ENTITY_TYPES][NUM_FLOAT_TYPES]) (entity *en, float_types type); + +void (*fn_set_local_entity_raw_vec3d[NUM_ENTITY_TYPES][NUM_VEC3D_TYPES]) (entity *en, vec3d_types type, vec3d *v); +void (*fn_set_local_entity_vec3d[NUM_ENTITY_TYPES][NUM_VEC3D_TYPES]) (entity *en, vec3d_types type, vec3d *v); +void (*fn_set_client_server_entity_vec3d[NUM_ENTITY_TYPES][NUM_VEC3D_TYPES][NUM_COMMS_MODEL_TYPES]) (entity *en, vec3d_types type, vec3d *v); +void (*fn_get_local_entity_vec3d[NUM_ENTITY_TYPES][NUM_VEC3D_TYPES]) (entity *en, vec3d_types type, vec3d *v); +vec3d *(*fn_get_local_entity_vec3d_ptr[NUM_ENTITY_TYPES][NUM_VEC3D_TYPES]) (entity *en, vec3d_types type); + +void (*fn_set_local_entity_ptr_value[NUM_ENTITY_TYPES][NUM_PTR_TYPES]) (entity *en, ptr_types type, void *ptr); +void *(*fn_get_local_entity_ptr_value[NUM_ENTITY_TYPES][NUM_PTR_TYPES]) (entity *en, ptr_types type); + +void (*fn_set_local_entity_first_child[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type, entity *first_child); +entity *(*fn_get_local_entity_first_child[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type); +void (*fn_set_local_entity_parent[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type, entity *parent); +entity *(*fn_get_local_entity_parent[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type); +void (*fn_set_local_entity_child_succ[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type, entity *child_succ); +entity *(*fn_get_local_entity_child_succ[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type); +void (*fn_set_local_entity_child_pred[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type, entity *child_pred); +entity *(*fn_get_local_entity_child_pred[NUM_ENTITY_TYPES][NUM_LIST_TYPES]) (entity *en, list_types type); + +void (*fn_update_client_server_entity[NUM_ENTITY_TYPES][NUM_COMMS_MODEL_TYPES]) (entity *en); -#include "eech_extracted.c" +int (*message_responses[NUM_ENTITY_TYPES][NUM_ENTITY_MESSAGES]) (entity_messages message, entity *receiver, entity *sender, va_list pargs); -#define MAX_ENTITIES 256 +/* name databases: only read on debug_fatal paths, which abort the harness */ +entity_type_data entity_type_database[NUM_ENTITY_TYPES]; +list_type_data list_type_database[NUM_LIST_TYPES]; +int_type_data int_type_database[NUM_INT_TYPES]; +float_type_data float_type_database[NUM_FLOAT_TYPES]; +vec3d_type_data vec3d_type_database[NUM_VEC3D_TYPES]; +ptr_type_data ptr_type_database[NUM_PTR_TYPES]; + +const char + *overload_invalid_int_type_message = "invalid int type", + *debug_fatal_invalid_int_type_message = "invalid int type", + *overload_invalid_float_type_message = "invalid float type", + *debug_fatal_invalid_float_type_message = "invalid float type", + *overload_invalid_list_type_message = "invalid list type", + *debug_fatal_invalid_list_type_message = "invalid list type", + *overload_invalid_vec3d_type_message = "invalid vec3d type", + *debug_fatal_invalid_vec3d_type_message = "invalid vec3d type", + *overload_invalid_ptr_type_message = "invalid ptr type", + *debug_fatal_invalid_ptr_type_message = "invalid ptr type"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// environment +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define MAX_HARNESS_ENTITIES 256 static entity - entities[MAX_ENTITIES]; + entity_storage[MAX_HARNESS_ENTITIES]; + +/* en_heap.h: get_local_entity_index (EN) is ((EN) - entities) */ +entity + *entities = entity_storage; static int num_entities; @@ -36,13 +128,23 @@ static int entity *session_entity; -int (*message_responses[NUM_ENTITY_TYPES][NUM_ENTITY_MESSAGES]) (entity_messages message, entity *receiver, entity *sender, va_list pargs); +comms_model_types + system_comms_model = COMMS_MODEL_SERVER; + +comms_data_flow_types + system_comms_data_flow = COMMS_DATA_FLOW_RX; + +int + command_line_entity_update_frame_rate = 2; + +FILE + *tacview_log_file = NULL; /* tacview logging is excluded: never logging */ static jmp_buf abort_operation; static char - labels[MAX_ENTITIES][32]; + labels[MAX_HARNESS_ENTITIES][32]; static void harness_fail (const char *what) { @@ -51,6 +153,7 @@ static void harness_fail (const char *what) exit (2); } +/* ASSERT runs in normal control flow (never in a signal handler) */ void harness_assert (const char *expression) { printf ("result assert %s\n", expression); @@ -58,247 +161,541 @@ void harness_assert (const char *expression) longjmp (abort_operation, 1); } -static void null_dereference (void) +/* raw data of entity types whose files are not compiled yet (hand-written rows below) */ + +typedef struct { - printf ("result null-dereference\n"); + entity_sub_types + sub_type; - longjmp (abort_operation, 1); -} + entity_sides + side; -static const char *label_of (entity *en) -{ - return en ? labels[en->index] : "NULL"; -} + int + in_use; -static unsigned int float_bits (float value) -{ - unsigned int - bits; + vec3d + position; - memcpy (&bits, &value, sizeof (bits)); + supply_type + supplies; +} shim_keysite; - return bits; -} +typedef struct +{ + entity_sides + side; +} shim_force; -static entity *new_entity (entity_types type, void *data, const char *label) +typedef struct { - entity - *en; + vec3d + position; /* physical state: supplied by the scenario */ +} shim_mobile; - if (num_entities >= MAX_ENTITIES) - { - harness_fail ("too many entities"); - } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// final state output and NULL dereference outcome +// +// The SIGSEGV handler's call graph contains no C library call except write () +// and _exit (): fixed strings have compile-time lengths, and float bits are +// read through a union and hex-encoded by hand. The same code writes the final +// state on the normal path, so both paths produce identical text. stdout is +// unbuffered (setvbuf in main), so nothing printed before a fault is lost. +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - en = &entities[num_entities]; +#define NULL_PAGE_SIZE 4096 - memset (en, 0, sizeof (*en)); +/* a string literal and its length, without the terminator */ +#define LITERAL(TEXT) (TEXT), (sizeof (TEXT) - 1) - en->type = type; - en->index = num_entities; - en->data = data; +static group + *final_group_raw; - snprintf (labels[num_entities], sizeof (labels[num_entities]), "%s", label); +static shim_keysite + *final_keysites; - num_entities++; +static int + final_keysite_count; - return en; +static void write_all (const char *text, size_t length) +{ + while (length > 0) + { + ssize_t written = write (STDOUT_FILENO, text, length); + + if (written <= 0) + { + _exit (3); + } + + text += written; + length -= (size_t) written; + } } -/* LIST_TYPE_GROUP_LINK: one link serves three list types (en_list/get_prnt.h) */ -static list_types link_slot (list_types type) +static void write_float_bits (float value) { - if ((type == LIST_TYPE_BUILDING_GROUP) || (type == LIST_TYPE_INDEPENDENT_GROUP)) + union { - return LIST_TYPE_KEYSITE_GROUP; + float + f; + + unsigned int + u; + } bits; + + char + hex[8]; + + int + i; + + bits.f = value; + + for (i = 7; i >= 0; i--) + { + hex[i] = "0123456789abcdef"[bits.u & 0xf]; + bits.u >>= 4; } - return type; + write_all (hex, sizeof (hex)); } -/* pointer updates of insert_local_entity_into_parents_child_list, without notifications */ -static void link_entity (entity *en, list_types type, entity *parent, entity *pred) +static void write_final_pair (const char *prefix, size_t prefix_length, float a, float b) { - entity - *succ; - - list_types - link = link_slot (type); - - succ = pred ? pred->child_succ[link] : parent->first_child[type]; + write_all (prefix, prefix_length); + write_float_bits (a); + write_all (LITERAL (" ")); + write_float_bits (b); + write_all (LITERAL ("\n")); +} - en->child_succ[link] = succ; - en->child_pred[link] = pred; - en->parent[link] = parent; +static void emit_final_state (void) +{ + int + i; - if (succ) + if (final_group_raw) { - succ->child_pred[link] = en; + write_final_pair (LITERAL ("final group "), final_group_raw->supplies.ammo_supply_level, final_group_raw->supplies.fuel_supply_level); } - if (pred) + for (i = 0; i < final_keysite_count; i++) { - pred->child_succ[link] = en; + write_final_pair (LITERAL ("final keysite "), final_keysites[i].supplies.ammo_supply_level, final_keysites[i].supplies.fuel_supply_level); } - else +} + +/* + * EECH dereferences NULL entity pointers without a check in release builds + * (e.g. get_local_entity_type (EN) is ((EN)->type)). The original code runs + * unguarded here too. A fault inside the NULL page is that outcome: it is + * reported, the final state is written and the process ends. It is never + * resumed. + * + * SA_RESETHAND has already restored the default action on entry, so for any + * other fault the handler simply returns: the faulting instruction re-executes + * and the process dies by SIGSEGV, which the test driver reports as a failure. + */ +static void segmentation_fault (int signal_number, siginfo_t *info, void *context) +{ + if ((uintptr_t) info->si_addr < NULL_PAGE_SIZE) { - parent->first_child[type] = en; + write_all (LITERAL ("result null-dereference\n")); + + emit_final_state (); + + _exit (0); } } -void *get_local_entity_data (entity *en) +static void install_segmentation_fault_handler (void) +{ + struct sigaction + action; + + memset (&action, 0, sizeof (action)); + + action.sa_sigaction = segmentation_fault; + + action.sa_flags = SA_SIGINFO | SA_RESETHAND; + + sigemptyset (&action.sa_mask); + + sigaction (SIGSEGV, &action, NULL); +} + +void debug_fatal (const char *string, ...) { - if (!en) null_dereference (); + fprintf (stderr, "harness: debug_fatal: %s\n", string); - return en->data; + exit (2); } -entity_types get_local_entity_type (entity *en) +void debug_log (const char *string, ...) { - if (!en) null_dereference (); +} - return en->type; +void debug_log_entity_message (entity_messages message, entity *receiver, entity *sender, va_list pargs) +{ } -entity *get_local_entity_first_child (entity *en, list_types type) +int tacview_reset_frame (void) { - if (!en) null_dereference (); + harness_fail ("tacview is excluded"); - return en->first_child[type]; + return 0; } -entity *get_local_entity_parent (entity *en, list_types type) +void write_tacview_frame_header (void) { - if (!en) null_dereference (); + harness_fail ("tacview is excluded"); +} - return en->parent[link_slot (type)]; +void add_group_type_to_force_info (entity *en, entity_sub_types group_type) +{ + harness_fail ("add_group_type_to_force_info (force.c) is not compiled"); } -entity *get_local_entity_child_succ (entity *en, list_types type) +void remove_group_type_from_force_info (entity *en, entity_sub_types group_type) { - if (!en) null_dereference (); + harness_fail ("remove_group_type_from_force_info (force.c) is not compiled"); +} + +int set_local_division_name (entity *en, char *s) +{ + harness_fail ("set_local_division_name (division.c) is not compiled"); - return en->child_succ[link_slot (type)]; + return 0; } -int get_local_entity_int_value (entity *en, int_types type) +static const char *label_of (entity *en) { - if (!en) null_dereference (); + return en ? labels[en - entities] : "NULL"; +} - switch (en->type) - { - case ENTITY_TYPE_GROUP: - { - group *raw = (group *) en->data; +static unsigned int float_bits (float value) +{ + unsigned int + bits; - if (type == INT_TYPE_RESUPPLY_SOURCE) return raw->resupply_source; - if (type == INT_TYPE_GROUP_MODE) return en->first_child[LIST_TYPE_GUIDE_STACK] ? GROUP_MODE_BUSY : GROUP_MODE_IDLE; - if (type == INT_TYPE_SIDE) return raw->side; + memcpy (&bits, &value, sizeof (bits)); - break; - } - case ENTITY_TYPE_KEYSITE: - { - keysite *raw = (keysite *) en->data; + return bits; +} - if (type == INT_TYPE_ENTITY_SUB_TYPE) return raw->sub_type; - if (type == INT_TYPE_IN_USE) return raw->in_use; - if (type == INT_TYPE_SIDE) return raw->side; +/* transport: en_comms.c transmit_entity_comms_message; only float values occur */ +void transmit_entity_comms_message (entity_comms_messages message, entity *en, ...) +{ + va_list + pargs; - break; - } - case ENTITY_TYPE_FORCE: - { - force *raw = (force *) en->data; + float_types + type; - if (type == INT_TYPE_SIDE) return raw->side; + float + value; - break; - } - default: - { - break; - } + if (message != ENTITY_COMMS_FLOAT_VALUE) + { + harness_fail ("unexpected entity comms message"); } - harness_fail ("int value not supplied by shim"); + va_start (pargs, en); - return 0; + type = va_arg (pargs, float_types); + + value = va_arg (pargs, double); + + va_end (pargs); + + printf ("transmit %s %d %08x\n", label_of (en), (int) type, float_bits (value)); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// fail-loud table defaults +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int unsupplied_get_int (entity *en, int_types type) { harness_fail ("int value not supplied"); return 0; } +static void unsupplied_set_int (entity *en, int_types type, int value) { harness_fail ("int set not supplied"); } +static float unsupplied_get_float (entity *en, float_types type) { harness_fail ("float value not supplied"); return 0.0f; } +static void unsupplied_set_float (entity *en, float_types type, float value) { harness_fail ("float set not supplied"); } +static vec3d *unsupplied_get_vec3d_ptr (entity *en, vec3d_types type) { harness_fail ("vec3d not supplied"); return NULL; } +static void *unsupplied_get_ptr (entity *en, ptr_types type) { harness_fail ("ptr value not supplied"); return NULL; } +static entity *unsupplied_get_list (entity *en, list_types type) { harness_fail ("list not supplied"); return NULL; } +static void unsupplied_set_list (entity *en, list_types type, entity *other) { harness_fail ("list set not supplied"); } +static void unsupplied_update (entity *en) { harness_fail ("update function not supplied"); } +static int unsupplied_message_response (entity_messages message, entity *receiver, entity *sender, va_list pargs) { harness_fail ("message response not supplied"); return FALSE; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// hand-written rows for entity types whose files are not compiled yet +// (docs/architecture.md, "Shrinking the C reference shim") +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/* list storage for shim entity types */ +static entity + *shim_first_child[MAX_HARNESS_ENTITIES][NUM_LIST_TYPES], + *shim_parent[MAX_HARNESS_ENTITIES][NUM_LIST_TYPES], + *shim_child_succ[MAX_HARNESS_ENTITIES][NUM_LIST_TYPES], + *shim_child_pred[MAX_HARNESS_ENTITIES][NUM_LIST_TYPES]; + +static entity *shim_get_first_child (entity *en, list_types type) { return shim_first_child[en - entities][type]; } +static void shim_set_first_child (entity *en, list_types type, entity *v) { shim_first_child[en - entities][type] = v; } +static entity *shim_get_parent (entity *en, list_types type) { return shim_parent[en - entities][type]; } +static void shim_set_parent (entity *en, list_types type, entity *v) { shim_parent[en - entities][type] = v; } +static entity *shim_get_child_succ (entity *en, list_types type) { return shim_child_succ[en - entities][type]; } +static void shim_set_child_succ (entity *en, list_types type, entity *v) { shim_child_succ[en - entities][type] = v; } +static entity *shim_get_child_pred (entity *en, list_types type) { return shim_child_pred[en - entities][type]; } +static void shim_set_child_pred (entity *en, list_types type, entity *v) { shim_child_pred[en - entities][type] = v; } + +static void shim_root (entity_types entity_type, list_types list) +{ + fn_get_local_entity_first_child[entity_type][list] = shim_get_first_child; + fn_set_local_entity_first_child[entity_type][list] = shim_set_first_child; } -static supply_type *supplies_of (entity *en) +static void shim_link (entity_types entity_type, list_types list) { - if (en->type == ENTITY_TYPE_GROUP) return &((group *) en->data)->supplies; - if (en->type == ENTITY_TYPE_KEYSITE) return &((keysite *) en->data)->supplies; + fn_get_local_entity_parent[entity_type][list] = shim_get_parent; + fn_set_local_entity_parent[entity_type][list] = shim_set_parent; + fn_get_local_entity_child_succ[entity_type][list] = shim_get_child_succ; + fn_set_local_entity_child_succ[entity_type][list] = shim_set_child_succ; + fn_get_local_entity_child_pred[entity_type][list] = shim_get_child_pred; + fn_set_local_entity_child_pred[entity_type][list] = shim_set_child_pred; +} + +/* ks_int.c, ks_float.c, ks_vec3d.c */ +static int shim_keysite_int (entity *en, int_types type) +{ + shim_keysite *raw = (shim_keysite *) get_local_entity_data (en); - harness_fail ("supplies not supplied by shim"); + if (type == INT_TYPE_ENTITY_SUB_TYPE) return raw->sub_type; + if (type == INT_TYPE_IN_USE) return raw->in_use; - return NULL; + harness_fail ("keysite int value not supplied"); + + return 0; } -float get_local_entity_float_value (entity *en, float_types type) +static float shim_keysite_float (entity *en, float_types type) { - if (!en) null_dereference (); + shim_keysite *raw = (shim_keysite *) get_local_entity_data (en); - if (type == FLOAT_TYPE_AMMO_SUPPLY_LEVEL) return supplies_of (en)->ammo_supply_level; - if (type == FLOAT_TYPE_FUEL_SUPPLY_LEVEL) return supplies_of (en)->fuel_supply_level; + return (type == FLOAT_TYPE_AMMO_SUPPLY_LEVEL) ? raw->supplies.ammo_supply_level : raw->supplies.fuel_supply_level; +} + +static void shim_keysite_set_server_float (entity *en, float_types type, float value) +{ + shim_keysite *raw = (shim_keysite *) get_local_entity_data (en); - harness_fail ("float value not supplied by shim"); + if (type == FLOAT_TYPE_AMMO_SUPPLY_LEVEL) raw->supplies.ammo_supply_level = value; + else raw->supplies.fuel_supply_level = value; - return 0.0f; + transmit_entity_comms_message (ENTITY_COMMS_FLOAT_VALUE, en, type, value); } -/* COMMS_MODEL_SERVER: set_server_float_value = set_local_float_value + transmit */ -void set_client_server_entity_float_value (entity *en, float_types type, float value) +static vec3d *shim_keysite_position (entity *en, vec3d_types type) { - if (!en) null_dereference (); + return &((shim_keysite *) get_local_entity_data (en))->position; +} - if (type == FLOAT_TYPE_AMMO_SUPPLY_LEVEL) supplies_of (en)->ammo_supply_level = value; - else if (type == FLOAT_TYPE_FUEL_SUPPLY_LEVEL) supplies_of (en)->fuel_supply_level = value; - else harness_fail ("float set not supplied by shim"); +/* fc_int.c */ +static int shim_force_int (entity *en, int_types type) +{ + return ((shim_force *) get_local_entity_data (en))->side; +} - printf ("transmit %s %d %08x\n", label_of (en), (int) type, float_bits (value)); +/* ac_vec3d.c: physical position from the scenario (environment) */ +static vec3d *shim_mobile_position (entity *en, vec3d_types type) +{ + return &((shim_mobile *) get_local_entity_data (en))->position; } -vec3d *get_local_entity_vec3d_ptr (entity *en, vec3d_types type) +static int record_force_low_on_supplies (entity_messages message, entity *receiver, entity *sender, va_list pargs) { - if (!en) null_dereference (); + int + sub_type = va_arg (pargs, int); + + printf ("message %s %s %d %d\n", label_of (receiver), label_of (sender), (int) message, sub_type); + + return FALSE; +} - if (type != VEC3D_TYPE_POSITION) harness_fail ("vec3d not supplied by shim"); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// initialisation (C: initialise_entity_functions, reduced to the adopted entity types) +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - switch (en->type) +static void initialise_tables (void) +{ + int + i, + j, + k; + + for (i = 0; i < NUM_ENTITY_TYPES; i++) { - case ENTITY_TYPE_KEYSITE: return &((keysite *) en->data)->position; - case ENTITY_TYPE_HELICOPTER: return &((mobile *) en->data)->position; - case ENTITY_TYPE_GROUP: + for (j = 0; j < NUM_INT_TYPES; j++) { - entity *leader = en->first_child[LIST_TYPE_MEMBER]; /* PTR_TYPE_GROUP_LEADER */ + /* en_int.c defaults: sets do nothing; gets are not supplied */ + fn_set_local_entity_raw_int_value[i][j] = harness_default_set_entity_int_value; + fn_set_local_entity_int_value[i][j] = harness_default_set_entity_int_value; + for (k = 0; k < NUM_COMMS_MODEL_TYPES; k++) fn_set_client_server_entity_int_value[i][j][k] = unsupplied_set_int; + fn_get_local_entity_int_value[i][j] = unsupplied_get_int; + } - return leader ? get_local_entity_vec3d_ptr (leader, type) : NULL; + for (j = 0; j < NUM_FLOAT_TYPES; j++) + { + /* en_float.c defaults: sets do nothing; gets are not supplied */ + fn_set_local_entity_raw_float_value[i][j] = harness_default_set_entity_float_value; + fn_set_local_entity_float_value[i][j] = harness_default_set_entity_float_value; + for (k = 0; k < NUM_COMMS_MODEL_TYPES; k++) fn_set_client_server_entity_float_value[i][j][k] = unsupplied_set_float; + fn_get_local_entity_float_value[i][j] = unsupplied_get_float; + } + + for (j = 0; j < NUM_VEC3D_TYPES; j++) fn_get_local_entity_vec3d_ptr[i][j] = unsupplied_get_vec3d_ptr; + + for (j = 0; j < NUM_PTR_TYPES; j++) fn_get_local_entity_ptr_value[i][j] = unsupplied_get_ptr; + + for (j = 0; j < NUM_LIST_TYPES; j++) + { + fn_get_local_entity_first_child[i][j] = unsupplied_get_list; + fn_set_local_entity_first_child[i][j] = unsupplied_set_list; + fn_get_local_entity_parent[i][j] = unsupplied_get_list; + fn_set_local_entity_parent[i][j] = unsupplied_set_list; + fn_get_local_entity_child_succ[i][j] = unsupplied_get_list; + fn_set_local_entity_child_succ[i][j] = unsupplied_set_list; + fn_get_local_entity_child_pred[i][j] = unsupplied_get_list; + fn_set_local_entity_child_pred[i][j] = unsupplied_set_list; } - default: break; + + for (k = 0; k < NUM_COMMS_MODEL_TYPES; k++) fn_update_client_server_entity[i][k] = unsupplied_update; + + for (j = 0; j < NUM_ENTITY_MESSAGES; j++) message_responses[i][j] = unsupplied_message_response; } - harness_fail ("position not supplied by shim"); + /* original overloads (compiled translation units) */ + overload_group_list_functions (); + overload_group_int_value_functions (); + overload_group_float_value_functions (); + overload_group_vec3d_functions (); + overload_group_ptr_value_functions (); + overload_group_update_functions (); + harness_overload_group_link_parent_responses (); + + overload_update_list_functions (); + overload_update_message_responses (); + harness_default_update_link_responses (); + + /* hand-written rows */ + shim_root (ENTITY_TYPE_SESSION, LIST_TYPE_FORCE); + + shim_link (ENTITY_TYPE_FORCE, LIST_TYPE_FORCE); + shim_root (ENTITY_TYPE_FORCE, LIST_TYPE_KEYSITE_FORCE); + shim_root (ENTITY_TYPE_FORCE, LIST_TYPE_INDEPENDENT_GROUP); + fn_get_local_entity_int_value[ENTITY_TYPE_FORCE][INT_TYPE_SIDE] = shim_force_int; + + shim_link (ENTITY_TYPE_KEYSITE, LIST_TYPE_KEYSITE_FORCE); + shim_root (ENTITY_TYPE_KEYSITE, LIST_TYPE_KEYSITE_GROUP); + shim_root (ENTITY_TYPE_KEYSITE, LIST_TYPE_BUILDING_GROUP); + fn_get_local_entity_int_value[ENTITY_TYPE_KEYSITE][INT_TYPE_ENTITY_SUB_TYPE] = shim_keysite_int; + fn_get_local_entity_int_value[ENTITY_TYPE_KEYSITE][INT_TYPE_IN_USE] = shim_keysite_int; + fn_get_local_entity_float_value[ENTITY_TYPE_KEYSITE][FLOAT_TYPE_AMMO_SUPPLY_LEVEL] = shim_keysite_float; + fn_get_local_entity_float_value[ENTITY_TYPE_KEYSITE][FLOAT_TYPE_FUEL_SUPPLY_LEVEL] = shim_keysite_float; + fn_set_client_server_entity_float_value[ENTITY_TYPE_KEYSITE][FLOAT_TYPE_AMMO_SUPPLY_LEVEL][COMMS_MODEL_SERVER] = shim_keysite_set_server_float; + fn_set_client_server_entity_float_value[ENTITY_TYPE_KEYSITE][FLOAT_TYPE_FUEL_SUPPLY_LEVEL][COMMS_MODEL_SERVER] = shim_keysite_set_server_float; + fn_get_local_entity_vec3d_ptr[ENTITY_TYPE_KEYSITE][VEC3D_TYPE_POSITION] = shim_keysite_position; + + shim_link (ENTITY_TYPE_GUIDE, LIST_TYPE_GUIDE_STACK); + + shim_link (ENTITY_TYPE_HELICOPTER, LIST_TYPE_MEMBER); + fn_get_local_entity_vec3d_ptr[ENTITY_TYPE_HELICOPTER][VEC3D_TYPE_POSITION] = shim_mobile_position; - return NULL; + message_responses[ENTITY_TYPE_FORCE][ENTITY_MESSAGE_FORCE_LOW_ON_SUPPLIES] = record_force_low_on_supplies; } -static int record_force_low_on_supplies (entity_messages message, entity *receiver, entity *sender, va_list pargs) +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// scenario construction +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static entity *new_entity (entity_types type, void *data, const char *label) { - int - sub_type = va_arg (pargs, int); + entity + *en; - printf ("message %s %s %d %d\n", label_of (receiver), label_of (sender), (int) message, sub_type); + if (num_entities >= MAX_HARNESS_ENTITIES) + { + harness_fail ("too many entities"); + } - return FALSE; + en = &entities[num_entities]; + + memset (en, 0, sizeof (*en)); + + set_local_entity_type (en, type); + set_local_entity_data (en, data); + + snprintf (labels[num_entities], sizeof (labels[num_entities]), "%s", label); + + num_entities++; + + return en; } -static int unsupplied_message_response (entity_messages message, entity *receiver, entity *sender, va_list pargs) +/* + * List membership as a restored campaign holds it: the pointer updates of + * insert_local_entity_into_parents_child_list through the dispatch tables, + * without its LINK_CHILD / LINK_PARENT notifications (the TS scenario + * builder's insertLocalEntityIntoParentsChildListRaw does the same). + */ +static void link_entity_raw (entity *en, list_types type, entity *parent, entity *pred) { - harness_fail ("message response not supplied by shim"); + entity + *succ; - return FALSE; + succ = pred ? get_local_entity_child_succ (pred, type) : get_local_entity_first_child (parent, type); + + set_local_entity_child_succ (en, type, succ); + set_local_entity_child_pred (en, type, pred); + set_local_entity_parent (en, type, parent); + + if (succ) + { + set_local_entity_child_pred (succ, type, en); + } + + if (pred) + { + set_local_entity_child_succ (pred, type, en); + } + else + { + set_local_entity_first_child (parent, type, en); + } +} + +static entity *last_child (entity *parent, list_types type) +{ + entity + *en, + *last = NULL; + + for (en = get_local_entity_first_child (parent, type); en; en = get_local_entity_child_succ (en, type)) + { + last = en; + } + + return last; } static char *next_token (char **cursor) @@ -316,43 +713,93 @@ static int next_int (char **cursor) { return atoi (next_token (cursor)); } static double next_double (char **cursor) { return strtod (next_token (cursor), NULL); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static group + harness_groups[MAX_HARNESS_ENTITIES]; + +static int + num_groups; + +static entity + *groups[MAX_HARNESS_ENTITIES]; + +static void print_timeline_state (int step, entity *update_root) +{ + entity + *en; + + int + i; + + printf ("step %d %08x ", step, float_bits (get_delta_time ())); + + en = get_local_entity_first_child (update_root, LIST_TYPE_UPDATE); + + if (!en) + { + printf ("-"); + } + + while (en) + { + printf ("%s%s", label_of (en), get_local_entity_child_succ (en, LIST_TYPE_UPDATE) ? "," : ""); + + en = get_local_entity_child_succ (en, LIST_TYPE_UPDATE); + } + + printf ("\n"); + + for (i = 0; i < num_groups; i++) + { + printf ("timer %08x %08x\n", float_bits (harness_groups[i].sleep), float_bits (harness_groups[i].assist_timer)); + } +} + int main (void) { - static force force_data[MAX_ENTITIES]; - static keysite keysite_data[MAX_ENTITIES]; - static group group_data; - static mobile leader_data; + static shim_force force_data[MAX_HARNESS_ENTITIES]; + static shim_keysite keysite_data[MAX_HARNESS_ENTITIES]; + static shim_mobile leader_data; + static update update_data; entity *session, - *forces[MAX_ENTITIES], - *keysites[MAX_ENTITIES], + *update_root, + *forces[MAX_HARNESS_ENTITIES], + *keysites[MAX_HARNESS_ENTITIES], *keysite_tail[NUM_ENTITY_SIDES] = { NULL }, *group_en = NULL; int num_forces = 0, num_keysites = 0, - i, - j; + timeline = FALSE, + step = 0, + i; char line[512], *cursor, *word; - for (i = 0; i < NUM_ENTITY_TYPES; i++) - { - for (j = 0; j < NUM_ENTITY_MESSAGES; j++) - { - message_responses[i][j] = unsupplied_message_response; - } - } + /* unbuffered: output written before a fault is never lost, and the fault + handler never needs to flush */ + setvbuf (stdout, NULL, _IONBF, 0); - message_responses[ENTITY_TYPE_FORCE][ENTITY_MESSAGE_FORCE_LOW_ON_SUPPLIES] = record_force_low_on_supplies; + initialise_tables (); + + install_segmentation_fault_handler (); + + final_keysites = keysite_data; session = new_entity (ENTITY_TYPE_SESSION, NULL, "session"); + update_root = new_entity (ENTITY_TYPE_UPDATE, &update_data, "update"); + + /* up_update.c :: set_update_entity (not compiled whole; it only assigns and logs) */ + update_entity = update_root; + while (fgets (line, sizeof (line), stdin)) { cursor = line; @@ -373,7 +820,7 @@ int main (void) forces[num_forces] = new_entity (ENTITY_TYPE_FORCE, &force_data[num_forces], label); - link_entity (forces[num_forces], LIST_TYPE_FORCE, session, num_forces ? forces[num_forces - 1] : NULL); + link_entity_raw (forces[num_forces], LIST_TYPE_FORCE, session, num_forces ? forces[num_forces - 1] : NULL); num_forces++; } @@ -381,7 +828,7 @@ int main (void) { char label[32]; - keysite *raw = &keysite_data[num_keysites]; + shim_keysite *raw = &keysite_data[num_keysites]; raw->side = (entity_sides) next_int (&cursor); raw->sub_type = next_int (&cursor); @@ -400,7 +847,7 @@ int main (void) { if (force_data[i].side == raw->side) { - link_entity (keysites[num_keysites], LIST_TYPE_KEYSITE_FORCE, forces[i], keysite_tail[raw->side]); + link_entity_raw (keysites[num_keysites], LIST_TYPE_KEYSITE_FORCE, forces[i], keysite_tail[raw->side]); keysite_tail[raw->side] = keysites[num_keysites]; @@ -409,17 +856,22 @@ int main (void) } num_keysites++; + + final_keysite_count = num_keysites; } else if (strcmp (word, "group") == 0) { + /* slice 1 group: raw fields as a restored campaign holds them */ int parent_kind, parent_index, busy, has_leader; - next_int (&cursor); /* group sub type: the shim receives its resupply_source directly */ + group *raw = &harness_groups[num_groups]; - group_data.resupply_source = next_int (&cursor); - group_data.side = (entity_sides) next_int (&cursor); - group_data.supplies.ammo_supply_level = next_double (&cursor); - group_data.supplies.fuel_supply_level = next_double (&cursor); + memset (raw, 0, sizeof (*raw)); + + raw->sub_type = next_int (&cursor); + raw->side = (entity_sides) next_int (&cursor); + raw->supplies.ammo_supply_level = next_double (&cursor); + raw->supplies.fuel_supply_level = next_double (&cursor); parent_kind = next_int (&cursor); parent_index = next_int (&cursor); @@ -430,19 +882,23 @@ int main (void) leader_data.position.y = 0.0; leader_data.position.z = next_double (&cursor); - group_en = new_entity (ENTITY_TYPE_GROUP, &group_data, "group"); + group_en = new_entity (ENTITY_TYPE_GROUP, raw, "group"); + + final_group_raw = raw; + + num_groups++; if (parent_kind == 1) { - link_entity (group_en, LIST_TYPE_KEYSITE_GROUP, keysites[parent_index], NULL); + link_entity_raw (group_en, LIST_TYPE_KEYSITE_GROUP, keysites[parent_index], NULL); } else if (parent_kind == 2) { for (i = 0; i < num_forces; i++) { - if (force_data[i].side == group_data.side) + if (force_data[i].side == raw->side) { - link_entity (group_en, LIST_TYPE_INDEPENDENT_GROUP, forces[i], NULL); + link_entity_raw (group_en, LIST_TYPE_INDEPENDENT_GROUP, forces[i], NULL); break; } @@ -451,12 +907,98 @@ int main (void) if (busy) { - link_entity (new_entity (ENTITY_TYPE_GUIDE, NULL, "guide"), LIST_TYPE_GUIDE_STACK, group_en, NULL); + link_entity_raw (new_entity (ENTITY_TYPE_GUIDE, NULL, "guide"), LIST_TYPE_GUIDE_STACK, group_en, NULL); } if (has_leader) { - link_entity (new_entity (ENTITY_TYPE_HELICOPTER, &leader_data, "leader"), LIST_TYPE_MEMBER, group_en, NULL); + link_entity_raw (new_entity (ENTITY_TYPE_HELICOPTER, &leader_data, "leader"), LIST_TYPE_MEMBER, group_en, NULL); + } + } + else if (strcmp (word, "timeline") == 0) + { + timeline = TRUE; + + command_line_entity_update_frame_rate = next_int (&cursor); + } + else if (strcmp (word, "tgroup") == 0) + { + /* timeline group: raw fields and update list membership as a restored campaign holds them */ + char label[32]; + + group *raw = &harness_groups[num_groups]; + + memset (raw, 0, sizeof (*raw)); + + raw->sub_type = next_int (&cursor); + raw->side = (entity_sides) next_int (&cursor); + raw->sleep = next_double (&cursor); + raw->assist_timer = next_double (&cursor); + + snprintf (label, sizeof (label), "group%d", num_groups); + + groups[num_groups] = new_entity (ENTITY_TYPE_GROUP, raw, label); + + if (next_int (&cursor)) + { + link_entity_raw (groups[num_groups], LIST_TYPE_UPDATE, update_root, last_child (update_root, LIST_TYPE_UPDATE)); + } + + num_groups++; + } + else if ((strcmp (word, "set") == 0) || (strcmp (word, "frame") == 0)) + { + int is_set = (strcmp (word, "set") == 0); + int group_index = 0, float_type = 0, locked = 0, count = 0, c; + double value, delta = 0.0; + + if (is_set) + { + group_index = next_int (&cursor); + float_type = next_int (&cursor); + value = next_double (&cursor); + } + else + { + delta = next_double (&cursor); + locked = next_int (&cursor); + count = next_int (&cursor); + } + + if (setjmp (abort_operation) != 0) + { + return 0; + } + + if (is_set) + { + set_client_server_entity_float_value (groups[group_index], (float_types) float_type, value); + } + else + { + /* environment: set_delta_time (time.c) measures the frame; the scenario supplies it */ + system_delta_time = delta; + system_one_over_delta_time = 1.0 / system_delta_time; + locked_frame_rate = locked; + + /* host flight loop (flight.c): one update per time acceleration step */ + for (c = 0; c < count; c++) + { + update_client_server_entities (); + } + } + + print_timeline_state (step, update_root); + + step++; + } + else if (strcmp (word, "end") == 0) + { + if (timeline) + { + printf ("result ok\n"); + + return 0; } } else if (strcmp (word, "op") == 0) @@ -514,6 +1056,25 @@ int main (void) printf ("range %08x %08x\n", float_bits (get_2d_range (&v1, &v2)), float_bits (get_approx_2d_range (&v1, &v2))); } + else if (strcmp (word, "fault-null") == 0) + { + /* harness self-test: a read inside the NULL page */ + volatile int *volatile null_pointer = NULL; + + printf ("read %d\n", null_pointer[1]); + } + else if (strcmp (word, "fault-unmapped") == 0) + { + /* harness self-test: a fault outside the NULL page must kill the process */ + volatile int *guard = mmap (NULL, 65536, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if ((void *) guard == MAP_FAILED) + { + harness_fail ("mmap failed"); + } + + printf ("read %d\n", guard[4096]); + } else { harness_fail ("unknown op"); @@ -522,15 +1083,7 @@ int main (void) printf ("result ok\n"); } - if (group_en) - { - printf ("final group %08x %08x\n", float_bits (group_data.supplies.ammo_supply_level), float_bits (group_data.supplies.fuel_supply_level)); - } - - for (i = 0; i < num_keysites; i++) - { - printf ("final keysite %08x %08x\n", float_bits (keysite_data[i].supplies.ammo_supply_level), float_bits (keysite_data[i].supplies.fuel_supply_level)); - } + emit_final_state (); return 0; } @@ -540,7 +1093,7 @@ int main (void) } } - harness_fail ("scenario has no op line"); + harness_fail ("scenario has no op or end line"); return 1; } diff --git a/eech-core-ts/docs/architecture.md b/eech-core-ts/docs/architecture.md index 045880ca2..9a0474449 100644 --- a/eech-core-ts/docs/architecture.md +++ b/eech-core-ts/docs/architecture.md @@ -1,6 +1,8 @@ # eech-core-ts architecture report -This is the bootstrap report for the frozen EECH campaign-core port (issue #1). +This is the architecture report for the frozen EECH campaign-core port. It was +written at bootstrap (issue #1, slice 1) and is kept current: slice 2 (issue #3) +added update timing, the `Clock` port and the first C-reference shim reduction. The original C under `aphavoc/` and `modules/` is the behavioural authority. The current `ee-dcs` implementation is not used as a reference anywhere. @@ -37,7 +39,8 @@ DCS objects standing in for it. ## 2. Which entity-system facilities does the kernel need? -The first slice needed these, and all of them are ported in `src/entity/system`: +Slices 1 and 2 needed these, and all of them are ported in `src/entity/system` +and `src/entity/special/update`: - identity, type and raw data (`entity.ts`; C: `en_main.c`) - per-type list roots and links, including **shared links**: a group's @@ -48,14 +51,19 @@ The first slice needed these, and all of them are ported in `src/entity/system`: - client/server dispatch, `set_client_server_entity_float_value [..][comms_model]`, with the server path "set local, then transmit" (`comms.ts`, `serverFloatValueSetter`) - messages: `notify_local_entity` and the `message_responses` table (`en_msgs.ts`) +- list insertion and deletion **with** link notifications (`en_list.c`, slice 2). + They can only be used on lists whose parent and child responses are ported: + group, keysite, task, regen and waypoint respond to these messages, and an + unported response fails loudly. +- the entity update loop and update dispatch (`up_update.c`, `en_updt.c`, + `LIST_TYPE_UPDATE`, slice 2) +- the campaign side of time: `get_delta_time`, `set_manual_delta_time` and + `locked_frame_rate` (`src/core/time.ts`, slice 2) The kernel will need these later. They are not ported yet: - creation and destruction with attribute lists (`en_creat.c`, `en_dstry.c`) -- list insertion with link notifications: `insert_local_entity_into_parents_child_list` - sends `ENTITY_MESSAGE_LINK_CHILD` / `LINK_PARENT`, and group, keysite, task, - regen and waypoint respond to those -- the update list and sleep timers (`en_updt.c`, `LIST_TYPE_UPDATE`) +- update functions of entity types other than the group - pack/unpack, i.e. save games (`en_pack.c`) - string values @@ -75,7 +83,7 @@ Measured over the kernel directories listed in question 1: | terrain (`get_3d_terrain_*`, `terrain_*`) | 158 in 17 files | Environmental. Becomes a future `Terrain` port. | | `set_client_server_entity_*` / `transmit_entity_comms_message` | 381 / 255 | Network transport, which DCS replaces. The semantic contract is "authoritative value changed". **Port: `EntityReplication`.** | | `create_/destroy_client_server_entity` | 109 in 31 files | Campaign-core entity lifecycle. It becomes a physical materialisation port where a mobile appears in or leaves the world. | -| `get_delta_time`, `get_system_time`, time acceleration | 40 in 6 files | Environmental. Future `Clock` port. | +| `get_delta_time`, `get_system_time`, time acceleration | 40 in 6 files | Environmental measurement. **Port: `Clock`** (slice 2). The campaign's override of the delta (`set_manual_delta_time`) is core. | | `frand1`, `sfrand1`, `rand16` | 48 in 14 files | Environmental. Future `RandomSource` port, which must reproduce EECH's generator sequence. | | sound and speech (`play_client_server_*`, speech) | 59 in 7 files | Physical/presentation. Excluded, or an optional notification port. | | flight dynamics, velocities | 20 in 5 files | Physical simulation, which DCS replaces. Excluded. Only the semantic observation (landed, taken off, arrived) crosses a port. | @@ -86,18 +94,17 @@ membership, type, side and position. See `src/entity/mobile/mobile.ts`. ## 4. Which ports emerge naturally? -The first slice introduced exactly two ports, both derived from C call sites: +The ports introduced so far, each derived from C call sites: | Port | C origin | Test adapter | |---|---|---| | `MobilePhysicalState.getMobilePosition` | `ac_vec3d.c` / `vh_vec3d.c` `get_local_vec3d_ptr (VEC3D_TYPE_POSITION)` | `InMemoryMobilePhysicalState` (positions come only from the scenario, never invented) | | `EntityReplication.transmitEntityFloatValue` | `set_server_float_value` → `transmit_entity_comms_message (ENTITY_COMMS_FLOAT_VALUE, ..)` | `RecordingEntityReplication` (ordered log) | +| `Clock.getDeltaTime`, `Clock.isFrameRateLocked` (slice 2) | `time.c :: set_delta_time` (frame measurement) and `locked_frame_rate` | `ScriptedClock` (the frame driver states each frame) | These are the next ports in order of need. None is created before a ported slice calls it. -- `Clock`: `get_delta_time`. The update loop decrements `FLOAT_TYPE_SLEEP` and - `assist_timer` (`gp_updt.c`). - `RandomSource`: `rand16`, `frand1`, `sfrand1`. The adapter must reproduce the EECH generator bit for bit, so the C harness can verify it. - `Terrain`: elevation and terrain type queries. @@ -109,14 +116,21 @@ calls it. - `CampaignStore`: loading campaign/population files and save games (`ai/faction` parsers, `en_pack.c`). -**On scheduling.** EECH has no callback scheduler. Time advances through a -per-frame `get_delta_time ()`. Entities on `LIST_TYPE_UPDATE` count down -`FLOAT_TYPE_SLEEP` and act when it expires, and the high-level AI runs from -session and force updates. The deterministic "virtual scheduler" is therefore a -`Clock` adapter driving the ported update list, not a callback queue. It is -introduced with the first slice that contains an update function (see question 10). -Its runaway guard will bound the frame count. Tests must not call the private -update functions directly. +**On scheduling (implemented in slice 2).** EECH has no callback scheduler. +Each frame the host measures time (`set_delta_time`), then calls +`update_client_server_entities ()` once per time-acceleration step. That walks +`LIST_TYPE_UPDATE`, calling each entity's update function, and splits the frame +into `(int) (delta * rate + 1)` equal float sub-steps. The deterministic +"virtual scheduler" is therefore: +- the `ScriptedClock` plus a host-loop driver (`test/scenarios/update-timeline.ts`); +- running the **ported** update loop, which executes the real campaign update + functions in list order. That includes entities that remove themselves during + the walk, and entities inserted at the head, which are not visited until the + next pass. + +The runaway guard is test-side (`validateTimeline`): it refuses frames that +would need more than 10,000 update passes, and never changes a result. Tests +never call private update functions directly. ## 5. Which function is the first vertical slice, and why? @@ -143,40 +157,46 @@ the `record` policy. Production uses `throw`. ## 6. What enables or blocks running the C implementation under a harness? -It works today, for this slice. `c-reference/extract.mjs` copies the original -definitions **verbatim** into `build/c-reference/eech_extracted.c`, with `#line` -directives back to the original files: the enums, the supply struct, the macros, -and the five functions `assess_group_supplies`, `get_closest_keysite`, -`get_local_force_entity`, `get_2d_range`, `get_approx_2d_range` and -`notify_local_entity`. `c-reference/harness.c` supplies the environment: entity -records, lists, the accessor values and a recording message response. It then -calls the original functions. It compiles with `-Wall -Werror`. +It works, and since slice 2 most of it is original code rather than shim. +`c-reference/extract.mjs` generates: +- **`build/c-reference/project.h`**: the harness environment, verbatim + fragments, and **whole original headers**, including every entity dispatch + macro and table declaration; +- **`eech_extracted.c`**: verbatim functions from files too large to compile + whole, each with a `#line` directive back to its original file. + +Nine original translation units then compile **unchanged**: `gp_int.c`, +`gp_float.c`, `gp_list.c`, `gp_vec3d.c`, `gp_ptr.c`, `gp_updt.c`, `gp_dbase.c`, +`up_list.c` and `up_msgs.c`. Dispatch goes through the original `fn_*` tables, +filled by the original `overload_*_functions ()`. The harness defines the +tables, the environment, and hand-written rows only for entity types whose files +are not compiled yet (see "Shrinking the C reference shim"). What enables this: -- the target functions reach the entity system only through accessor macros, so - a shim can stand in for it; -- `DEBUG_MODULE` / `DEBUG_SUPPLY` logging is compiled out; -- the extractor fails loudly if a signature or enum disappears. +- EECH's headers are well layered; a curated include order plus a few fragments + satisfies the original files. No reconstruction of `project.h` was needed + (`docs/slices/group-update-timing.md`, investigation 3). +- `DEBUG_MODULE` / `DEBUG_SUPPLY` logging is compiled out. +- The extractor and the build fail loudly if a definition disappears or a + prototype is missing (`-Werror=implicit-function-declaration`). What limits it or blocks it next: -- **Accessor overloads are shim-provided.** Files such as `gp_int.c` are `static` - switches inside translation units that include `project.h`, the whole game. The - harness therefore verifies the slice functions, not `gp_int.c`/`ks_int.c` - themselves. Those accessors are small and are verified by source reading. To - execute them too, the next step is to compile the real `xx_int.c`/`xx_float.c` - files against a reduced `project.h`. +- **Keysite, force and session accessors are still hand-written.** Compiling + `ks_*.c`, `fc_*.c` and `ss_*.c` needs their headers in the reduced + `project.h`, and fail-loud stubs for their other dependencies. This is the + next shim-reduction step. - **Floating point.** The harness requires `FLT_EVAL_METHOD == 0`, i.e. IEEE single precision evaluated at the declared type (x86-64 SSE, arm64). The historical 32-bit MSVC x87 builds could differ in the last bit where intermediates stayed in extended precision. The port follows the IEEE model. - **Varargs messages.** They work unchanged. The shim installs the response for the one message it observes, and any other message aborts the harness. -- **Global state** (`session_entity`, `message_responses`) is defined by the - harness. Slices that use `get_delta_time`, `rand16` or terrain need those - environment functions in the shim, driven by the same scenario as the TS - adapters. +- **Global state** (`session_entity`, the dispatch tables, `entities`) is + defined by the harness. The frame delta (`set_delta_time`) is supplied from + the same scenario as the TS `ScriptedClock`. Slices that use `rand16` or + terrain need those environment functions in the harness, driven the same way. ## 7. Which coverage tooling enforces 100% reachable TS coverage? @@ -198,8 +218,9 @@ check without adding a branch to the caller. - `npm run test:lua` transpiles `src/` plus the shared scenarios with TSTL (`tsconfig.lua-test.json`) and **executes them in a real Lua 5.1 interpreter**: - the 41-case matrix, 250 scenarios whose outcomes were recorded from the - executed C, and the float32 edge cases. That is 313 checks. The runner refuses + both behaviour matrices (41 supply cases, 22 timeline cases), 250 scenarios + and 150 timelines whose outcomes were recorded from the executed C, and the + float32 edge cases. That is 485 checks. The runner refuses to run on anything but `_VERSION == "Lua 5.1"`, the version DCS embeds. - `npm run smoke:lua` loads the production bundle `build/lua/eech-core.lua` from plain Lua with Lua-table ports, as a DCS host would, and runs a campaign @@ -217,8 +238,9 @@ check without adding a branch to the caller. 2. `{ ...defaults, leader: undefined }` keeps the default in Lua, because a table cannot hold `nil`. Optional scenario values are therefore explicit variants. -- One mutant only fails under Lua: `in_use` tested by JavaScript truthiness. It - proves the Lua run adds detection power beyond the JavaScript run. +- Two mutants only fail under Lua: `in_use` (slice 1) and the timer setters' + `value != 0.0` (slice 2), each tested by JavaScript truthiness. They prove the + Lua run adds detection power beyond the JavaScript run. ## 9. How is C→TS migration tracked? @@ -235,10 +257,8 @@ check without adding a branch to the caller. ## 10. What should the next slices be? -1. **Group update and sleep timers** (`gp_updt.c :: update_server`, - `en_updt.c`). This introduces the `Clock` port and the deterministic update - loop, i.e. the virtual scheduler, with a runaway guard. It is small, and it is - the prerequisite for anything time-driven. +1. ~~**Group update and sleep timers**~~ Done in slice 2 + (`docs/slices/group-update-timing.md`). 2. **`keysite.c :: update_keysite_cargo`** (the second sender of `FORCE_LOW_ON_SUPPLIES`). It reuses this slice's runtime and extends keysite accessors. @@ -249,9 +269,12 @@ check without adding a branch to the caller. 4. **The `mb_msgs.c` landing handlers that call `assess_group_supplies`**, which introduce the `LandingObservation` port. The DCS adapter reports "member landed at keysite", and the campaign does the accounting. -5. **Harness depth.** Compile the real `gp_int.c`/`ks_int.c`/`ks_float.c` against - a reduced `project.h`, so the accessors are C-verified as well. See +5. **Harness depth.** The group files are done. Next, compile `ks_*.c` and + `fc_int.c`, ideally together with item 2, which touches the keysite. See "Shrinking the C reference shim" below. +6. **The readers of `sleep`**, e.g. task assignment and the `mb_msgs.c` landing + handler that sets `sleep` after rearming. These give slice 2's timers their + campaign meaning. ## Shrinking the C reference shim @@ -264,43 +287,70 @@ terrain, physical positions and the comms transport. ### Current shim surface -Each entry is written by hand in `harness.c` and mirrors behaviour that exists in -a real EECH file: +The harness builds a reduced `project.h` from the harness environment, verbatim +fragments and **whole original headers**, and compiles original translation +units unchanged (`c-reference/extract.mjs`, `REAL_TRANSLATION_UNITS`). Dispatch +goes through the original `fn_*` tables, filled by the original +`overload_*_functions ()`. -| Shim entry | Real EECH source that should replace it | +**Retired** (now executed from the original C): + +| Retired shim entry | Original code now executed | Slice | +|---|---|---| +| Group `INT_TYPE_RESUPPLY_SOURCE`, `INT_TYPE_GROUP_MODE`, `INT_TYPE_SIDE` | `gp_int.c` | 2 | +| Group `resupply_source` passed in by the scenario | `gp_dbase.c :: group_database` | 2 | +| Group supply level get/set, timer get/set | `gp_float.c` | 2 | +| Group position and leader | `gp_vec3d.c`, `gp_ptr.c` | 2 | +| Group list roots and links, including the shared `group_link` | `gp_list.c` with `en_list/*.h` | 2 | +| Hand-written NULL checks | the original unguarded code; a `SIGSEGV` is the reported outcome | 2 | + +**Remaining** (hand-written in `harness.c`): + +| Shim entry | Original source that should replace it | |---|---| -| Group `INT_TYPE_RESUPPLY_SOURCE`, `INT_TYPE_GROUP_MODE`, `INT_TYPE_SIDE` | `gp_int.c :: get_local_int_value` | -| Group `resupply_source` passed in by the scenario | `gp_dbase.c :: group_database` | -| Keysite `INT_TYPE_ENTITY_SUB_TYPE`, `INT_TYPE_IN_USE`, `INT_TYPE_SIDE` | `ks_int.c :: get_local_int_value` | +| Keysite `INT_TYPE_ENTITY_SUB_TYPE`, `INT_TYPE_IN_USE` | `ks_int.c :: get_local_int_value` | +| Keysite supply level get and server set | `ks_float.c` | +| Keysite `VEC3D_TYPE_POSITION` | `ks_vec3d.c` | | Force `INT_TYPE_SIDE` | `fc_int.c :: get_local_int_value` | -| Group and keysite supply level get/set | `gp_float.c`, `ks_float.c` (`get_local_float_value`, `set_local_float_value`, `set_server_float_value`) | -| Group, keysite and mobile `VEC3D_TYPE_POSITION` | `gp_vec3d.c`, `ks_vec3d.c`, and `gp_ptr.c` for the group leader. The mobile position itself stays shim-supplied: it is physical state. | -| List roots, links and the shared `group_link` | `en_list/get_frst.h`, `get_prnt.h`, `get_succ.h` included by the real `xx_list.c` files | -| Dispatch through the `fn_*` tables | `en_int.c`, `en_float.c`, `en_vec3d.c`, `en_ptr.c` (tables, defaults and dispatch macros) | +| List storage of session, force, keysite, guide and helicopter | `ss_list.c`, `fc_list.c`, `ks_list.c`, `gd_list.c`, `ac_list.c` | +| Delivery of `FORCE_LOW_ON_SUPPLIES` (recorded, not handled) | `fc_msgs.c :: response_to_force_low_on_supplies`. This is the slice 1 message boundary, and is ported with that response. | + +**Environment** (legitimately hand-written, driven by the same scenario data as +the TS adapters): +- the frame delta and locked flag (`set_delta_time`); +- the host loop's time acceleration; +- the comms model and data flow; +- the transport (`transmit_entity_comms_message`); +- the mobile position (physical); +- debug output; +- tacview (never logging); +- the Windows SDK `min`/`max`. + +**Fail-loud stubs** for code that is reachable only outside the adopted +behaviour: `add/remove_group_type_to/from_force_info`, `set_local_division_name`, +and every dispatch-table default except the C default setters, which are +extracted verbatim. ### Order of work -1. **Reduced `project.h`.** Create a harness-only header that satisfies the real - `xx_int.c`/`xx_float.c` files: the entity struct layouts they read, the - `fn_*` tables, and stubs for unrelated subsystems that fail loudly if called. - The real layout structs (`group`, `keysite`, `force`) come from their - original headers where possible. -2. **Value accessors first.** Compile `gp_int.c`, `ks_int.c`, `fc_int.c`, - `gp_float.c` and `ks_float.c` into the harness, and call their - `overload_*_functions ()` so dispatch goes through the real tables. Delete the - matching shim branches. -3. **Database.** Compile `gp_dbase.c` so `resupply_source` comes from the real - `group_database`, not from the scenario. -4. **Lists.** Compile the real `xx_list.c` files with their `en_list/*.h` - includes, so the shared `group_link` aliasing is executed from the original C. -5. **Positions.** Compile `gp_vec3d.c`, `gp_ptr.c` and `ks_vec3d.c`. The mobile - position stays a scenario-supplied shim entry, because that value is - physical. +1. ~~**Reduced `project.h`.**~~ Done in slice 2. It needed no reconstruction of + `project.h`, only whole original headers plus a few fragments (see + `docs/slices/group-update-timing.md`, investigation 3). +2. **Value accessors.** Group done (slice 2). Next: compile `ks_int.c`, + `ks_float.c` and `fc_int.c`, which need `keysite.h` / `force.h` in the + reduced `project.h`. +3. ~~**Database.**~~ `gp_dbase.c` is done (slice 2). +4. **Lists.** Group done (slice 2). Next: `ks_list.c`, `fc_list.c`, + `ss_list.c`, `gd_list.c`. +5. **Positions.** Group done (slice 2). Next: `ks_vec3d.c`. The mobile position + stays a scenario-supplied entry, because that value is physical. Each step lands with the slice that first needs it, or as its own small PR. Every step must keep the existing C reference cases and the recorded random -fixture passing unchanged. If an expectation changes, that is a finding about -the TS port, and must be investigated before anything is re-recorded. +fixtures passing unchanged. If an expectation changes, that is a finding about +the TS port, and must be investigated before anything is re-recorded. When +slice 2 replaced the group shim, slice 1's 41 cases passed unchanged, and +re-recording its 250-scenario fixture produced a byte-identical file. ### Rules for new slices @@ -319,8 +369,12 @@ the TS port, and must be investigated before anything is re-recorded. A slice is **frozen** when it has complete source mapping, 100% reachable coverage, source-derived expectations, passing TSTL/Lua execution, and a C -reference comparison where practical. `assess_group_supplies` and -`get_closest_keysite` meet all of these (see the manifest). After freezing: +reference comparison where practical. Frozen so far (see the manifest): +- slice 1: `assess_group_supplies` and `get_closest_keysite`; +- slice 2: group update timing (`update_server`, the timer setters, the update + loop). + +After freezing: - A behavioural change needs evidence from the EECH C, and the behaviour matrix and C reference must agree with it. diff --git a/eech-core-ts/docs/port-manifest.md b/eech-core-ts/docs/port-manifest.md index 9fd0000bd..b8e3d1359 100644 --- a/eech-core-ts/docs/port-manifest.md +++ b/eech-core-ts/docs/port-manifest.md @@ -30,6 +30,14 @@ where practical. | `aphavoc/source/entity/special/group/gp_ptr.c` | `src/entity/special/group/group.ts` | partial | | `aphavoc/source/entity/special/group/gp_vec3d.c` | `src/entity/special/group/group.ts` | partial | | `aphavoc/source/entity/special/group/gp_list.c` | `src/entity/special/group/group.ts` | partial | +| `aphavoc/source/entity/special/group/gp_updt.c` | `src/entity/special/group/group.ts` (`updateServer`) | ported | +| `aphavoc/source/entity/special/group/gp_msgs.c` | `src/entity/special/group/group.ts` (link/unlink parent responses) | partial | +| `aphavoc/source/entity/special/update/up_update.c` | `src/entity/special/update/update.ts` | partial (tacview and mobile bitsets excluded) | +| `aphavoc/source/entity/special/update/up_msgs.c` | `src/entity/special/update/update.ts` | ported | +| `aphavoc/source/entity/special/update/up_list.c` | `src/entity/special/update/update.ts` | ported | +| `aphavoc/source/entity/system/en_funcs/en_updt.c` | `src/entity/system/en_updt.ts` | ported (default handler deliberately not installed) | +| `modules/system/time.c` | `src/core/time.ts`, `src/ports/clock.ts` (port) | partial (measurement is the `Clock` port; delta history not ported) | +| `aphavoc/source/cmndline.c` (`command_line_entity_update_frame_rate`) | `src/core/cmndline.ts` | partial | | `aphavoc/source/entity/special/group/gp_dbase.c` | `src/generated/c-group-database.ts` (generated) | partial | | `aphavoc/source/entity/special/keysite/keysite.c` | `src/entity/special/keysite/keysite.ts` | partial | | `aphavoc/source/entity/special/keysite/ks_int.c`, `ks_float.c`, `ks_vec3d.c`, `ks_list.c` | `src/entity/special/keysite/keysite.ts` | partial | @@ -68,24 +76,47 @@ where practical. | `en_msgs.c :: notify_local_entity` | `notifyLocalEntity` | ported, tested, 100%-covered, C-reference-verified | | `miscmath.h :: bound` | `bound` | ported, tested, 100%-covered, C-reference-verified (through the slice) | -### Accessor overloads reached by the slice +### Frozen slice: group update timing (slice 2) -In the C harness these accessors are supplied by the shim, so they are verified -by source reading only (see architecture question 6). The plan for replacing -the shim with the real translation units, and the rules for new slices, are in +| C function | TS | Status | +|---|---|---| +| `gp_updt.c :: update_server`, `overload_group_update_functions` | `updateServer` | ported, tested, 100%-covered, C-reference-verified | +| `gp_float.c :: set_local_float_value` (`FLOAT_TYPE_SLEEP`, `FLOAT_TYPE_ASSIST_TIMER`), `set_server_float_value` | `setLocalTimerValue` via `serverFloatValueSetter` | ported, tested, 100%-covered, C-reference-verified | +| `gp_float.c :: get_local_float_value` (`FLOAT_TYPE_SLEEP`, `FLOAT_TYPE_ASSIST_TIMER`) | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | +| `up_update.c :: update_client_server_entities` | `updateClientServerEntities` | ported, tested, 100%-covered, C-reference-verified (tacview excluded) | +| `up_update.c :: set_entity_update_frame_rate` | `setEntityUpdateFrameRate` | ported, tested, 100%-covered, C-reference-verified | +| `up_update.c :: set_update_entity`, `get_update_entity`, `get/set_update_succ` | `update.ts` | ported, tested, 100%-covered | +| `up_msgs.c :: response_to_unlink_child` | `responseToUnlinkChild` | ported, tested, 100%-covered, C-reference-verified (successor fix-up branch: unit test only; no ported update function unlinks its successor) | +| `up_msgs.c` C-default link responses (`LINK_CHILD`, `LINK_PARENT`, `UNLINK_PARENT`) | `defaultMessageResponse` | ported, tested, 100%-covered, C-reference-verified | +| `gp_msgs.c :: response_to_link_parent` | `responseToLinkParent` | partial: the `LIST_TYPE_DIVISION` case (`set_local_division_name`) is unported and throws | +| `gp_msgs.c :: response_to_unlink_parent` | `responseToUnlinkParent` | ported, tested, 100%-covered, C-reference-verified | +| `en_list.c :: insert_local_entity_into_parents_child_list` | `insertLocalEntityIntoParentsChildList` | ported, tested, 100%-covered, C-reference-verified (the `#ifdef DEBUG` validation is kept, with debug-build meaning) | +| `en_list.c :: delete_local_entity_from_parents_child_list` | `deleteLocalEntityFromParentsChildList` | ported, tested, 100%-covered, C-reference-verified | +| `en_updt.c` dispatch (`update_client_server_entity`) | `updateClientServerEntity` | ported, tested, 100%-covered | +| `en_int.c :: default_set_entity_int_value` (group `INT_TYPE_UPDATED`) | `defaultSetEntityIntValue` | ported, tested, 100%-covered, C-reference-verified | +| `time.c :: set_manual_delta_time`, `get_delta_time`, `locked_frame_rate` | `src/core/time.ts` | ported, tested, 100%-covered, C-reference-verified (history and `system_one_over_delta_time` not ported: no ported reader) | +| `time.c :: set_delta_time`, `lock_frame_rate` | `Clock` port | blocked-engine-boundary (frame measurement) | +| Windows SDK `max` | `max` in `miscmath.ts` | ported, tested, 100%-covered | + +### Accessor overloads reached by the slices + +Since slice 2 the harness compiles the original group translation units, so +group accessors are `C-reference-verified`. Keysite, force, session, guide and +mobile accessors are still supplied by the harness shim, and are verified by +source reading only. The plan and the rules for new slices are in `docs/architecture.md`, "Shrinking the C reference shim". | C | TS | Status | |---|---|---| -| `gp_int.c :: get_local_int_value` (`INT_TYPE_GROUP_MODE`, `INT_TYPE_RESUPPLY_SOURCE`, `INT_TYPE_SIDE`) | `overloadGroupFunctions` | ported, tested, 100%-covered, source-read | +| `gp_int.c :: get_local_int_value` (`INT_TYPE_GROUP_MODE`, `INT_TYPE_RESUPPLY_SOURCE`, `INT_TYPE_SIDE`) | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | | `gp_int.c` (all other int types) | none | unported | -| `gp_float.c :: set_local_float_value`, `set_server_float_value` (`AMMO_SUPPLY_LEVEL`, `FUEL_SUPPLY_LEVEL`) | `overloadGroupFunctions` | ported, tested, 100%-covered, source-read | -| `gp_float.c :: set_client_float_value`, all other float types | none | unported | -| `gp_ptr.c :: get_local_ptr_value (PTR_TYPE_GROUP_LEADER)` | `overloadGroupFunctions` | ported, tested, 100%-covered, source-read | +| `gp_float.c :: set_local_float_value`, `set_server_float_value` (`AMMO_SUPPLY_LEVEL`, `FUEL_SUPPLY_LEVEL`) | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | +| `gp_float.c :: set_client_float_value`, the remaining float types | none | unported | +| `gp_ptr.c :: get_local_ptr_value (PTR_TYPE_GROUP_LEADER)` | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | | `gp_ptr.c :: set_local_ptr_value (PTR_TYPE_GROUP_LEADER)` | none | unported | -| `gp_vec3d.c :: get_local_vec3d_ptr (VEC3D_TYPE_POSITION)` | `overloadGroupFunctions` | ported, tested, 100%-covered, source-read | -| `gp_list.c` / `en_list/*.h`: `member_root`, `guide_stack_root`, `group_link` (BUILDING/INDEPENDENT/KEYSITE_GROUP) | `overloadGroupFunctions` | ported, tested, 100%-covered, source-read | -| `gp_dbase.c :: group_database[].resupply_source` | `GROUP_DATABASE_RESUPPLY_SOURCE` | ported (generated from C, drift-checked) | +| `gp_vec3d.c :: get_local_vec3d_ptr (VEC3D_TYPE_POSITION)` | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | +| `gp_list.c` / `en_list/*.h`: `member_root`, `guide_stack_root`, `group_link` (BUILDING/INDEPENDENT/KEYSITE_GROUP), `update_link` | `overloadGroupFunctions` | ported, tested, 100%-covered, C-reference-verified | +| `gp_dbase.c :: group_database[].resupply_source` | `GROUP_DATABASE_RESUPPLY_SOURCE` | ported (generated from C, drift-checked), C-reference-verified (the harness reads the compiled `group_database`) | | `ks_int.c :: get_local_int_value` (`ENTITY_SUB_TYPE`, `IN_USE`) | `overloadKeysiteFunctions` | ported, tested, 100%-covered, source-read | | `ks_float.c :: get_local_float_value`, `set_server_float_value` (`AMMO_SUPPLY_LEVEL`, `FUEL_SUPPLY_LEVEL`) | `overloadKeysiteFunctions` | ported, tested, 100%-covered, source-read | | `ks_vec3d.c :: get_local_vec3d_ptr (VEC3D_TYPE_POSITION)` | `overloadKeysiteFunctions` | ported, tested, 100%-covered, source-read | @@ -103,14 +134,14 @@ the shim with the real translation units, and the rules for new slices, are in | C | TS | Status | |---|---|---| | `en_list.c :: get_local_entity_first_child`, `get_local_entity_parent`, `get_local_entity_child_succ` | `en_list.ts` | ported, tested, 100%-covered | -| `en_list.c :: get_local_entity_child_pred` | none | unported (no adopted caller) | -| `en_list.c :: insert_local_entity_into_parents_child_list` | `insertLocalEntityIntoParentsChildListRaw` (pointer updates only, no `LINK_CHILD`/`LINK_PARENT` notifications) | partial | -| `en_list.c :: delete_local_entity_from_parents_child_list` and the other list functions | none | unported | +| `en_list.c :: get_local_entity_child_pred` | `getLocalEntityChildPred` | ported, tested, 100%-covered | +| `en_list.c :: insert_local_entity_into_parents_child_list` | `insertLocalEntityIntoParentsChildList` (slice 2), and `insertLocalEntityIntoParentsChildListRaw` for restoring state without notifications | ported | +| the other `en_list.c` functions | none | unported | | `en_main.c :: get_local_entity_type`, `get_local_entity_data` | `entity.ts` | ported, tested, 100%-covered | -| `en_creat.c`, `en_dstry.c`, `en_pack.c`, `en_updt.c` | `createLocalEntityRaw` (restore primitive only) | unported | +| `en_creat.c`, `en_dstry.c`, `en_pack.c` | `createLocalEntityRaw` (restore primitive only) | unported | | `en_int.c`, `en_float.c`, `en_vec3d.c`, `en_ptr.c` dispatch macros | `en_values.ts` | ported, tested, 100%-covered | | `en_*.c :: default_*` handlers | the unported sentinel (throws) instead of EECH defaults | deliberate: see architecture question 2 | -| `en_msgs.c :: default_message_response` | none | unported (no response proven to use it yet) | +| `en_msgs.c :: default_message_response` | `defaultMessageResponse` | ported; installed only where the C table keeps the default (the update entity's link responses) | | `en_valid.h :: validate_client_server_local_fn / remote_fn` | none | excluded (debug-build dispatch validation) | | client comms model overloads (`set_client_float_value`) | none | unported (the core is the server authority) | @@ -122,6 +153,11 @@ the shim with the real translation units, and the rules for new slices, are in `EechAssertionError` quoting the C expression. Where the C dereferences NULL without an assert (`get_closest_keysite` with no force for the side), the port throws `EechNullDereferenceError`. EECH itself would crash in both cases. +- **`max`** is the Windows SDK macro, not `Math.max`: `max (NaN, 0.0f)` is `0.0f`. +- **Findings about the original C** (recorded, not fixed): `time.c :: + set_manual_delta_time` has an undefined-behaviour history index update, and + `up_update.c` clears only a quarter of its entity bitsets. Neither affects + ported behaviour; see `docs/slices/group-update-timing.md`. - **Float arithmetic** follows IEEE single precision at declared type (`FLT_EVAL_METHOD == 0`). Historical x87 builds may differ in the last bit. - **`debug_log`** under `DEBUG_MODULE` / `DEBUG_SUPPLY` is compiled out in EECH diff --git a/eech-core-ts/docs/slices/group-update-timing.md b/eech-core-ts/docs/slices/group-update-timing.md new file mode 100644 index 000000000..7557e70e6 --- /dev/null +++ b/eech-core-ts/docs/slices/group-update-timing.md @@ -0,0 +1,231 @@ +# Slice 2: group update timing (issue #3) + +**Status:** frozen. All conformance gates pass (see "Evidence" at the end). + +The investigation is recorded first, because issue #3 makes it a gate before +the boundary is fixed. + +## Investigation 1: `gp_updt.c :: update_server` + +```c +if (raw->sleep > 0.0) { raw->sleep -= get_delta_time (); raw->sleep = max (raw->sleep, 0.0f); } +if (raw->assist_timer > 0.0) { raw->assist_timer -= get_delta_time (); raw->assist_timer = max (raw->assist_timer, 0.0f); } +if ((raw->sleep == 0.0) && (raw->assist_timer == 0.0)) delete_local_entity_from_parents_child_list (en, LIST_TYPE_UPDATE); +``` + +- **Sleep and assist timer.** Two independent float countdowns. A timer is only + decremented while it is `> 0.0`, so a negative value is never touched. After a + decrement it is clamped with `max (x, 0.0f)`. +- **`max` comes from the platform.** It is not defined anywhere in the EECH + repository: it is the Windows SDK macro `(((a) > (b)) ? (a) : (b))`. For a NaN + timer it yields `0.0f`, where `Math.max` / `fmax` would not. The port must use + the macro's expression. +- **`get_delta_time ()`** is the macro `(system_delta_time)` from + `modules/system/time.h`. The update loop may have overridden it with a + sub-step (investigation 2). +- **At or past the boundary.** A timer that reaches or crosses 0 becomes exactly + 0. When **both** timers are 0 (including one that was never set, or was + negative and so never decremented and never 0), the group removes itself from + `LIST_TYPE_UPDATE`. With a negative timer the group stays on the list for + ever, and that is EECH's behaviour. +- **Downstream effects.** Expiry causes no guide, task or AI change. The only + side effect is the list removal, which notifies: + - `UNLINK_CHILD` to the update entity. `up_msgs.c` fixes the loop's successor + pointer. + - `UNLINK_PARENT` to the group. `gp_msgs.c :: response_to_unlink_parent` only + calls `debug_log_entity_message`. +- **Entering the list.** This happens in `gp_float.c :: set_local_float_value`: + setting `FLOAT_TYPE_SLEEP` or `FLOAT_TYPE_ASSIST_TIMER` to a non-zero value, + when the group is not already on the update list, calls + `insert_local_entity_into_parents_child_list (en, LIST_TYPE_UPDATE, get_update_entity (), NULL)`. + That inserts at the **head** and notifies: + - `LINK_CHILD` to the update entity: C default response, because `up_msgs.c` + only overloads it under `DEBUG_MODULE`. + - `LINK_PARENT` to the group: `gp_msgs.c :: response_to_link_parent`, which + only acts for `LIST_TYPE_DIVISION`. +- **Readers of the timers are out of scope.** Task assignment, and the + `mb_msgs.c` landing handler that sets `sleep` after rearming, are separate + campaign behaviour. There is no natural boundary to cut *inside* + `update_server`, because it is already self-contained. + +**Classification.** +- Campaign core: the timers, the update-list membership and the list + notifications. +- Environmental: the frame delta. +- Not ported: the debug logging. + +## Investigation 2: the update-list semantics + +`en_updt.c` only holds the dispatch table `fn_update_client_server_entity[type][comms_model]`, +whose default is a no-op. The loop is `update/up_update.c :: update_client_server_entities`, +called by the host flight loop (`flight.c`) **once per time-acceleration step** +each frame. It is called zero times while paused, and `set_delta_time ()` runs +at the end of each frame. + +- **Traversal.** The loop walks `LIST_TYPE_UPDATE` under the update entity from + its first child. Before each entity's update it stores the successor in the + global `update_succ`, then continues from `update_succ`. +- **Ordering.** List order. Insertion is at the head, so an entity inserted + during a pass is not visited in that pass. An entity that removes itself is + safe, because its successor was saved beforehand. If the successor itself is + unlinked, the update entity's `UNLINK_CHILD` response advances `update_succ`. +- **Frame subdivision.** When the frame rate is not locked, the loop calls + `set_entity_update_frame_rate (command_line_entity_update_frame_rate)`. That + asserts `1 <= rate <= 100`, computes + `iterations = (int) (get_delta_time () * rate + 1.0)`, and sets the delta to + `get_delta_time () / iterations` through `set_manual_delta_time`. The loop then + makes `iterations` passes over the list, and finally restores the frame delta + with `set_manual_delta_time (delta_time)`. + - With a locked frame rate there is exactly one pass, and `set_manual_delta_time` + is refused. + - The rate is the EECH.INI setting "entity update frame rate". Its default is 2 + (`cmndline.c`). +- **`INT_TYPE_UPDATED`.** After each entity update the loop sets + `INT_TYPE_UPDATED`. For groups this is the `en_int.c` default setter, a + no-op; the mobiles' bitset is out of scope. +- **Global state:** + - `update_entity` and `update_succ` (update module) + - `system_delta_time`, `system_one_over_delta_time` and `locked_frame_rate` + (time module) + - `command_line_entity_update_frame_rate` (configuration) +- **Environment versus core.** + - Environment: measuring the frame (`set_delta_time`: `timeGetTime`, the + 0.001 s floor, the history ring, the locked frame rate) and the host loop's + time acceleration. + - Campaign core: `get_delta_time` and `set_manual_delta_time`, because the + update loop reads and overrides the delta. + - Excluded: tacview logging (`tacview_is_logging`), which is off. +- **No callback scheduler.** EECH's temporal behaviour is this per-frame walk of + the update list. The deterministic test boundary is therefore: + - a scripted `Clock`, which supplies each frame's delta and locked flag; + - a host-loop driver, which calls the real update loop `count` times per frame. + + The runaway guard is test-side only: it refuses a scenario whose frame would + need more sub-steps than a fixed limit, and it never changes a result. + +**Findings about the original C** (recorded, not "fixed"): +- **`time.c :: set_manual_delta_time`** writes + `system_delta_time_history_position = (++system_delta_time_history_position) % DELTA_TIME_HISTORY_SIZE`, + which is undefined behaviour in C. It only affects the delta history (read by + `get_delta_time_average`), which is not ported. +- **`up_update.c`** clears its bitsets with + `memset (x, 0, MAX_NUM_ENTITIES / (sizeof (unsigned int) * 8))`, a byte count + equal to the element count. Only a quarter of each array is cleared. This only + affects the mobiles' `INT_TYPE_UPDATED` / moved / rotated bits, which are out of + scope. + +## Investigation 3: compiling the real `gp_int.c` + +**Result: tractable, and done.** No reconstruction of `project.h` was needed. + +`build/c-reference/project.h` is generated by `c-reference/extract.mjs` from +four kinds of content: +- the harness environment, `c-reference/eech_harness_env.h`: C library, the + Windows SDK `min`/`max`, `ASSERT`, `debug_fatal`/`debug_log` declarations, two + engine types only named in prototypes (`matrix3x3`, `viewpoint`), and no-op + debug-build comms validation; +- verbatim fragments: + - `struct VEC3D`, `bound`, `METRE`, `KILOMETRE` (`modules/maths`) + - the time globals and `get_delta_time` (`modules/system/time.h`) + - the comms model and data-flow enums, globals and accessors (`comms.h`; + including the whole file would pull in the DirectPlay transport) + - two map enums used as database values (`ui_menu/.../map.h`) + - the session global and a few prototypes (`force.h`, `keysite.h`); +- **whole original headers**: `en_types.h`, `en_heap.h`, `en_msgs.h`, + `en_funcs.h` (all value and list dispatch macros and tables), `en_comms.h`, + `en_dbmsg.h`, `ai_extrn.h`, `up_update.h`, `division.h`, `group.h`, + `tacview.h`; +- `c-reference/eech_harness_decls.h`: the configuration global and the + prototypes of extracted functions. + +Nine original translation units then compile **unchanged**: `gp_int.c`, +`gp_float.c`, `gp_list.c`, `gp_vec3d.c`, `gp_ptr.c`, `gp_updt.c`, `gp_dbase.c`, +`up_list.c`, `up_msgs.c`. +- **Link surface:** the dispatch tables (defined by the harness, filled by the + original `overload_*_functions ()`), name databases read only on fatal paths, + `entities` (so `get_local_entity_index` is genuine pointer arithmetic), and + stubs that fail loudly: `add/remove_group_type_to/from_force_info`, + `set_local_division_name`, and tacview. +- **Needs extraction instead of whole-file compilation:** `en_list.c` + insert/delete, the `en_int.c`/`en_float.c` default setters, `en_msgs.c`, the + update loop in `up_update.c`, `time.c :: set_manual_delta_time`, and two + responses from `gp_msgs.c`. +- **Why those files aren't compiled whole:** they drag in sizeable unrelated + subsystems, such as the name databases of `en_int.c`, mobile tacview frame + handling, and the whole message table of `gp_msgs.c`. + +Slice 1's 41 C reference cases and 250 recorded scenarios passed **unchanged** +against this oracle (commit `e5c34da`). + +## Selected boundary + +**Ported to TypeScript:** +- `gp_updt.c :: update_server` and `overload_group_update_functions`. +- `gp_float.c` `FLOAT_TYPE_SLEEP` / `FLOAT_TYPE_ASSIST_TIMER`: get, local set + (with insertion into the update list), and server set. +- `up_update.c :: update_client_server_entities` and + `set_entity_update_frame_rate`, with `update_entity` / `update_succ`. +- `up_msgs.c :: response_to_unlink_child`, the C-default link responses of the + update entity, and `up_list.c` (`LIST_TYPE_UPDATE_ROOT`). +- `en_list.c :: insert_local_entity_into_parents_child_list` and + `delete_local_entity_from_parents_child_list`, with their notifications. Only + list types whose responses are ported can be used. +- `gp_msgs.c :: response_to_link_parent` (the `LIST_TYPE_DIVISION` case stays + unported) and `response_to_unlink_parent`. +- `en_updt.c` dispatch, and the `en_int.c` default setter for + `INT_TYPE_UPDATED` on groups. +- `modules/system/time.c`: `get_delta_time`, `set_manual_delta_time` and + `locked_frame_rate`, without the delta history, which has no ported reader. +- **New port `Clock`:** the frame delta and the locked flag, which is what + `set_delta_time` measures. +- **New option:** `entityUpdateFrameRate`, EECH.INI "entity update frame rate", + default 2. + +**Not in this slice:** +- whatever reads `sleep` / `assist_timer` (task assignment, landing and combat + handlers); +- update functions of any other entity type: reaching one fails as unported; +- tacview; +- the delta-time history and average. + +**C reference work:** the timeline scenarios run the original loop, update +function, setters, list code and time override. The shim entries retired by +this slice are listed in `docs/architecture.md`, "Shrinking the C reference +shim". + +## Behaviour matrix + +Every row is a case in `test/scenarios/update-timeline.cases.ts`. Each case runs +three ways: under JavaScript, under Lua 5.1, and through the executed original C. + +| Area | EECH behaviour | Cases | +|---|---|---| +| Timer setters | a non-zero value inserts the group at the **head** of the update list (negative values too); 0 does not; a group already on the list is not re-inserted; values narrow to float and are transmitted | `sleep-set-inserts-and-transmits`, `assist-set-inserts-at-head`, `set-zero-does-not-insert`, `set-negative-inserts`, `set-while-on-list-does-not-reinsert`, `set-narrows-to-float` | +| `update_server` | only a timer `> 0.0` is decremented; results clamp at 0; the group leaves the list when **both** timers are 0, exactly or after overshoot; a negative timer never expires | `sleep-counts-down`, `expiry-exactly-at-zero-leaves-update-list`, `overshoot-clamps-to-zero`, `both-timers-must-expire`, `negative-timer-is-never-decremented-and-never-expires`, `zero-timers-on-list-leave-on-first-update` | +| Update walk | the successor is saved first, so a group can remove itself mid-walk | `self-removal-does-not-break-the-walk` | +| Frame subdivision | `(int) (delta * rate + 1)` passes of `delta / passes` in float; the frame delta is restored | `frame-is-subdivided-into-equal-float-sub-steps`, `sub-step-float-rounding` | +| Host loop | time acceleration repeats the update; paused frames update nothing but still set the delta | `time-acceleration-repeats-the-update`, `paused-frame-updates-nothing` | +| Locked frame rate | one pass with the whole delta; the rate is not consulted | `locked-frame-rate-is-not-subdivided`, `frame-rate-not-used-when-locked` | +| Frame rate range | `ASSERT ((frame_rate >= 1) && (frame_rate <= 100))`, only when the loop actually runs unlocked | `frame-rate-below-range-asserts`, `frame-rate-above-range-asserts`, `frame-rate-not-used-when-paused` | + +Primitives that the group behaviour cannot reach by itself are covered by +isolated unit tests with C-derived expectations (`test/unit/update-runtime.test.ts`): +- `set_manual_delta_time` refused while locked; +- the C `(int)` cast on negative values; +- the `en_list.c` debug "already in list" check; +- deleting an entity that is in no list; +- the update entity's successor fix-up; +- the group's `LIST_TYPE_DIVISION` link response (unported). + +## Evidence + +| Check | Result | +|---|---| +| 22 timeline cases, JavaScript (`npm test`) | pass | +| the same 22 cases against the executed original C (`npm run test:cref`) | pass: expectations were derived by hand and all matched on the first run | +| 1,000 fresh random timelines, TS == C | pass (the generator is required to reach expiry, subdivision, locking and the rate assertion) | +| 150 random timelines recorded from the C, replayed in JavaScript and Lua 5.1 | pass | +| slice 1: 41 cases and 250 recorded scenarios against the new, less shimmed oracle | pass **unchanged**; re-recording the fixture was byte-identical | +| Lua 5.1 conformance (`npm run test:lua`) | 485 / 485 | +| coverage (statements / branches / functions / lines) | 100 / 100 / 100 / 100, no exclusions | +| mutation controls (`npm run mutation`) | 23 / 23 killed, including 10 new slice 2 mutants; the timer setters' truthiness mutant is caught only by Lua | diff --git a/eech-core-ts/scripts/mutation-check.mjs b/eech-core-ts/scripts/mutation-check.mjs index 92abc6ef7..b54eefb61 100644 --- a/eech-core-ts/scripts/mutation-check.mjs +++ b/eech-core-ts/scripts/mutation-check.mjs @@ -110,6 +110,77 @@ const MUTANTS = [ to: "if ((getLocalEntityIntValue(current_keysite, IntType.INT_TYPE_IN_USE) as unknown as boolean)) {", suite: "lua", }, + { + name: "update passes use the whole frame delta (sub-step never set)", + file: "src/entity/special/update/update.ts", + from: "\tsetManualDeltaTime(entity_update_delta_time);\n", + to: "\n", + suite: "js", + }, + { + name: "frame delta is not restored after the update passes", + file: "src/entity/special/update/update.ts", + from: "\tif (!isFrameRateLocked()) {\n\t\tsetManualDeltaTime(delta_time);\n\t}", + to: "", + suite: "js", + }, + { + name: "sub-step count rounds instead of truncating", + file: "src/entity/special/update/update.ts", + from: "return value < 0 ? Math.ceil(value) : Math.floor(value);", + to: "return Math.round(value);", + suite: "js", + }, + { + name: "a locked frame rate is still subdivided", + file: "src/entity/special/update/update.ts", + from: "\tlet delta_time = 0;\n\n\tif (!isFrameRateLocked()) {", + to: "\tlet delta_time = 0;\n\n\tif (true) {", + suite: "js", + }, + { + name: "the update walk reads the successor after the update (self-removal breaks the walk)", + file: "src/entity/special/update/update.ts", + from: "\t\t\tsetUpdateSucc(getLocalEntityChildSucc(en, ListType.LIST_TYPE_UPDATE));\n\n\t\t\tupdateClientServerEntity(en);", + to: "\t\t\tupdateClientServerEntity(en);\n\n\t\t\tsetUpdateSucc(getLocalEntityChildSucc(en, ListType.LIST_TYPE_UPDATE));", + suite: "js", + }, + { + name: "a group leaves the update list when either timer expires", + file: "src/entity/special/group/group.ts", + from: "if (raw.sleep === 0.0 && raw.assist_timer === 0.0) {", + to: "if (raw.sleep === 0.0 || raw.assist_timer === 0.0) {", + suite: "js", + }, + { + name: "an overshooting sleep timer is not clamped to zero", + file: "src/entity/special/group/group.ts", + from: "\t\traw.sleep = max(raw.sleep, 0.0);\n", + to: "\n", + suite: "js", + }, + { + name: "timer setters only insert positive values into the update list", + file: "src/entity/special/group/group.ts", + from: "if (value !== 0.0 && !getLocalEntityParent(en, ListType.LIST_TYPE_UPDATE)) {", + to: "if (value > 0.0 && !getLocalEntityParent(en, ListType.LIST_TYPE_UPDATE)) {", + suite: "js", + }, + { + name: "the sleep and assist timer accessors are swapped", + file: "src/entity/special/group/group.ts", + from: "fnGetLocalEntityFloatValue.overload(GROUP, FloatType.FLOAT_TYPE_SLEEP, (en) => getLocalEntityData(en).sleep);", + to: "fnGetLocalEntityFloatValue.overload(GROUP, FloatType.FLOAT_TYPE_SLEEP, (en) => getLocalEntityData(en).assist_timer);", + suite: "js", + }, + { + name: "timer setters test the value by JavaScript truthiness (0 is true in Lua)", + file: "src/entity/special/group/group.ts", + from: "if (value !== 0.0 && !getLocalEntityParent(en, ListType.LIST_TYPE_UPDATE)) {", + // hidden from TypeScript so only the Lua semantics can catch it + to: "if ((value as unknown as boolean) && !getLocalEntityParent(en, ListType.LIST_TYPE_UPDATE)) {", + suite: "lua", + }, ]; function run(cwd, command, args) { diff --git a/eech-core-ts/src/core/assert.ts b/eech-core-ts/src/core/assert.ts index 26d066a7c..faff61a90 100644 --- a/eech-core-ts/src/core/assert.ts +++ b/eech-core-ts/src/core/assert.ts @@ -13,6 +13,14 @@ export class EechAssertionError extends Error { } } +// Raised where EECH calls debug_fatal (debug builds abort). +export class EechFatalError extends Error { + public constructor(message: string) { + super(message); + this.name = "EechFatalError"; + } +} + // Raised where the original C dereferences a NULL pointer without an ASSERT. // EECH would crash; the port refuses to continue. export class EechNullDereferenceError extends Error { diff --git a/eech-core-ts/src/core/cmndline.ts b/eech-core-ts/src/core/cmndline.ts new file mode 100644 index 000000000..d5c7b08c2 --- /dev/null +++ b/eech-core-ts/src/core/cmndline.ts @@ -0,0 +1,18 @@ +// +// Campaign configuration that EECH reads from the command line / EECH.INI. +// +// C provenance: aphavoc/source/cmndline.c, eechini.c +// + +// C: command_line_entity_update_frame_rate = 2.0 (cmndline.c); EECH.INI "entity update frame rate" +export const DEFAULT_ENTITY_UPDATE_FRAME_RATE = 2; + +let command_line_entity_update_frame_rate = DEFAULT_ENTITY_UPDATE_FRAME_RATE; + +export function getCommandLineEntityUpdateFrameRate(): number { + return command_line_entity_update_frame_rate; +} + +export function setCommandLineEntityUpdateFrameRate(frame_rate: number): void { + command_line_entity_update_frame_rate = frame_rate; +} diff --git a/eech-core-ts/src/core/maths/miscmath.ts b/eech-core-ts/src/core/maths/miscmath.ts index 396013bb1..d5ee710ad 100644 --- a/eech-core-ts/src/core/maths/miscmath.ts +++ b/eech-core-ts/src/core/maths/miscmath.ts @@ -8,6 +8,12 @@ export function bound(value: number, lower: number, upper: number): number { return value < lower ? lower : value > upper ? upper : value; } +// C provenance: Windows SDK windef.h :: #define max(a,b) (((a) > (b)) ? (a) : (b)) +// (EECH relies on the platform macro). Unlike Math.max, max (NaN, 0) is 0. +export function max(a: number, b: number): number { + return a > b ? a : b; +} + // C provenance: constant.h :: #define METRE (1.0f), #define KILOMETRE (1000 * METRE) export const METRE = 1.0; diff --git a/eech-core-ts/src/core/time.ts b/eech-core-ts/src/core/time.ts new file mode 100644 index 000000000..d2582e5e2 --- /dev/null +++ b/eech-core-ts/src/core/time.ts @@ -0,0 +1,61 @@ +// +// Delta time as the campaign sees it. +// +// C provenance: modules/system/time.c, modules/system/time.h +// +// float system_delta_time = 0.1, system_one_over_delta_time = 10.0; +// int locked_frame_rate = FALSE; +// #define get_delta_time() (system_delta_time) +// void set_manual_delta_time (float delta_time); +// +// set_delta_time () measures the frame and belongs to the environment (the +// Clock port). The entity update loop reads and overrides the delta, so that +// part is core state. +// +// Not ported: system_one_over_delta_time and the delta history (and hence +// get_delta_time_average), which no ported code reads. +// + +import { toFloat32 } from "./float32"; + +export interface FrameTime { + getDeltaTime(): number; + isFrameRateLocked(): boolean; +} + +let system_delta_time = toFloat32(0.1); + +let locked_frame_rate = false; + +// C: static initialisers of time.c +export function resetDeltaTime(): void { + system_delta_time = toFloat32(0.1); + locked_frame_rate = false; +} + +// C provenance: time.h :: #define get_delta_time() (system_delta_time) +export function getDeltaTime(): number { + return system_delta_time; +} + +// C provenance: time.h :: extern int locked_frame_rate +export function isFrameRateLocked(): boolean { + return locked_frame_rate; +} + +// C provenance: time.c :: set_manual_delta_time (the float parameter narrows) +export function setManualDeltaTime(delta_time: number): void { + if (!locked_frame_rate) { + system_delta_time = toFloat32(delta_time); + } + + // else: debug_log ("TIME: cannot set locked delta time") +} + +// C provenance: time.c :: set_delta_time. The measurement itself is the +// environment's: the Clock port supplies its result. +export function setDeltaTimeFrom(clock: FrameTime): void { + locked_frame_rate = clock.isFrameRateLocked(); + + system_delta_time = toFloat32(clock.getDeltaTime()); +} diff --git a/eech-core-ts/src/entity/special/group/group.ts b/eech-core-ts/src/entity/special/group/group.ts index 90c836fcf..31b7ffb37 100644 --- a/eech-core-ts/src/entity/special/group/group.ts +++ b/eech-core-ts/src/entity/special/group/group.ts @@ -2,12 +2,14 @@ // Group entity. // // C provenance: entity/special/group/group.c, gp_int.c, gp_float.c, gp_vec3d.c, -// gp_ptr.c, gp_list.c, gp_dbase.c +// gp_ptr.c, gp_list.c, gp_dbase.c, gp_updt.c, gp_msgs.c // import { ASSERT } from "../../../core/assert"; import { toFloat32 } from "../../../core/float32"; -import { bound, KILOMETRE } from "../../../core/maths/miscmath"; +import { UnportedBehaviourError } from "../../../core/assert"; +import { bound, KILOMETRE, max } from "../../../core/maths/miscmath"; +import { getDeltaTime } from "../../../core/time"; import { AMMO_USAGE_ACCELERATOR, FUEL_USAGE_ACCELERATOR } from "../../../generated/c-constants"; import { GROUP_DATABASE_RESUPPLY_SOURCE } from "../../../generated/c-group-database"; import { @@ -26,10 +28,21 @@ import { ResupplySourceType, Vec3dType, } from "../../../generated/c-enums"; -import { getLocalEntityFirstChild, getLocalEntityParent, overloadEntityListLink, overloadEntityListRoot } from "../../system/en_list"; -import { notifyLocalEntity } from "../../system/en_msgs"; import { + deleteLocalEntityFromParentsChildList, + getLocalEntityFirstChild, + getLocalEntityParent, + insertLocalEntityIntoParentsChildList, + overloadEntityListLink, + overloadEntityListRoot, +} from "../../system/en_list"; +import { messageResponses, notifyLocalEntity, type MessageResponseFn } from "../../system/en_msgs"; +import { fnUpdateClientServerEntity } from "../../system/en_updt"; +import { + defaultSetEntityIntValue, + fnGetLocalEntityFloatValue, fnGetLocalEntityIntValue, + fnSetLocalEntityIntValue, fnGetLocalEntityPtrValue, fnGetLocalEntityVec3dPtr, fnSetClientServerEntityFloatValue, @@ -43,12 +56,15 @@ import { import { getLocalEntityData, getLocalEntityType, type Entity } from "../../system/entity"; import { getLocalForceEntity } from "../force/force"; import { getClosestKeysite, type SupplyRaw } from "../keysite/keysite"; +import { getUpdateEntity } from "../update/update"; // C provenance: group.h :: struct GROUP (ported fields only) export interface GroupRaw { sub_type: EntitySubTypeGroup; side: EntitySide; supplies: SupplyRaw; + sleep: number; + assist_timer: number; } // @@ -151,6 +167,58 @@ export function assessGroupSupplies(en: Entity): void { } } +// C provenance: gp_updt.c :: update_server +function updateServer(en: Entity): void { + const raw = getLocalEntityData(en); + + // + // Group is only on update list when its sleeping or under attack + // + + if (raw.sleep > 0.0) { + raw.sleep = toFloat32(raw.sleep - getDeltaTime()); + + raw.sleep = max(raw.sleep, 0.0); + } + + if (raw.assist_timer > 0.0) { + raw.assist_timer = toFloat32(raw.assist_timer - getDeltaTime()); + + raw.assist_timer = max(raw.assist_timer, 0.0); + } + + if (raw.sleep === 0.0 && raw.assist_timer === 0.0) { + deleteLocalEntityFromParentsChildList(en, ListType.LIST_TYPE_UPDATE); + } +} + +// C provenance: gp_float.c :: set_local_float_value (FLOAT_TYPE_SLEEP, FLOAT_TYPE_ASSIST_TIMER) +function setLocalTimerValue(en: Entity, type: FloatType, value: number): void { + const raw = getLocalEntityData(en); + + if (type === FloatType.FLOAT_TYPE_SLEEP) { + raw.sleep = value; + } else { + raw.assist_timer = value; + } + + if (value !== 0.0 && !getLocalEntityParent(en, ListType.LIST_TYPE_UPDATE)) { + insertLocalEntityIntoParentsChildList(en, ListType.LIST_TYPE_UPDATE, getUpdateEntity(), undefined); + } +} + +// C provenance: gp_msgs.c :: response_to_link_parent +const responseToLinkParent: MessageResponseFn = (_message, _receiver, _sender, args) => { + if ((args[0] as ListType) === ListType.LIST_TYPE_DIVISION) { + throw new UnportedBehaviourError("gp_msgs.c :: response_to_link_parent (LIST_TYPE_DIVISION) -> set_local_division_name"); + } + + return 1; +}; + +// C provenance: gp_msgs.c :: response_to_unlink_parent (only debug_log_entity_message) +const responseToUnlinkParent: MessageResponseFn = () => 1; + export function overloadGroupFunctions(): void { const GROUP = EntityType.ENTITY_TYPE_GROUP; @@ -159,6 +227,7 @@ export function overloadGroupFunctions(): void { overloadEntityListRoot(GROUP, "member_root", [ListType.LIST_TYPE_MEMBER]); overloadEntityListRoot(GROUP, "guide_stack_root", [ListType.LIST_TYPE_GUIDE_STACK]); overloadEntityListLink(GROUP, "group_link", [ListType.LIST_TYPE_BUILDING_GROUP, ListType.LIST_TYPE_INDEPENDENT_GROUP, ListType.LIST_TYPE_KEYSITE_GROUP]); + overloadEntityListLink(GROUP, "update_link", [ListType.LIST_TYPE_UPDATE]); // C provenance: gp_int.c :: get_local_int_value fnGetLocalEntityIntValue.overload(GROUP, IntType.INT_TYPE_GROUP_MODE, (en) => @@ -167,6 +236,13 @@ export function overloadGroupFunctions(): void { fnGetLocalEntityIntValue.overload(GROUP, IntType.INT_TYPE_RESUPPLY_SOURCE, (en) => GROUP_DATABASE_RESUPPLY_SOURCE[getLocalEntityData(en).sub_type]); fnGetLocalEntityIntValue.overload(GROUP, IntType.INT_TYPE_SIDE, (en) => getLocalEntityData(en).side); + // C provenance: gp_int.c does not overload INT_TYPE_UPDATED; en_int.c's default setter applies + fnSetLocalEntityIntValue.overload(GROUP, IntType.INT_TYPE_UPDATED, defaultSetEntityIntValue); + + // C provenance: gp_float.c :: get_local_float_value + fnGetLocalEntityFloatValue.overload(GROUP, FloatType.FLOAT_TYPE_SLEEP, (en) => getLocalEntityData(en).sleep); + fnGetLocalEntityFloatValue.overload(GROUP, FloatType.FLOAT_TYPE_ASSIST_TIMER, (en) => getLocalEntityData(en).assist_timer); + // C provenance: gp_float.c :: set_local_float_value, set_server_float_value const server = fnSetClientServerEntityFloatValue[CommsModelType.COMMS_MODEL_SERVER]; @@ -186,6 +262,17 @@ export function overloadGroupFunctions(): void { }), ); + server.overload(GROUP, FloatType.FLOAT_TYPE_SLEEP, serverFloatValueSetter(setLocalTimerValue)); + + server.overload(GROUP, FloatType.FLOAT_TYPE_ASSIST_TIMER, serverFloatValueSetter(setLocalTimerValue)); + + // C provenance: gp_updt.c :: overload_group_update_functions + fnUpdateClientServerEntity.overload(GROUP, CommsModelType.COMMS_MODEL_SERVER, updateServer); + + // C provenance: gp_msgs.c :: overload_group_message_responses (LINK_PARENT, UNLINK_PARENT only) + messageResponses.overload(GROUP, EntityMessage.ENTITY_MESSAGE_LINK_PARENT, responseToLinkParent); + messageResponses.overload(GROUP, EntityMessage.ENTITY_MESSAGE_UNLINK_PARENT, responseToUnlinkParent); + // C provenance: gp_ptr.c :: get_local_ptr_value (PTR_TYPE_GROUP_LEADER) fnGetLocalEntityPtrValue.overload(GROUP, PtrType.PTR_TYPE_GROUP_LEADER, (en) => getLocalEntityFirstChild(en, ListType.LIST_TYPE_MEMBER)); diff --git a/eech-core-ts/src/entity/special/update/update.ts b/eech-core-ts/src/entity/special/update/update.ts new file mode 100644 index 000000000..72e676d4c --- /dev/null +++ b/eech-core-ts/src/entity/special/update/update.ts @@ -0,0 +1,141 @@ +// +// The update entity and the entity update loop. +// +// C provenance: entity/special/update/up_update.c, up_update.h, up_msgs.c, up_list.c +// +// EECH has no callback scheduler. Each frame the host calls +// update_client_server_entities () once per time-acceleration step; it walks +// LIST_TYPE_UPDATE under the update entity and calls each entity's update +// function, subdividing the frame's delta time into equal sub-steps. +// +// Not ported: tacview frame logging (excluded), and the mobiles' +// moved / rotated / updated bitsets (no ported mobile update). +// + +import { ASSERT } from "../../../core/assert"; +import { getCommandLineEntityUpdateFrameRate } from "../../../core/cmndline"; +import { toFloat32 } from "../../../core/float32"; +import { getDeltaTime, isFrameRateLocked, setManualDeltaTime } from "../../../core/time"; +import { EntityMessage, EntityType, IntType, ListType } from "../../../generated/c-enums"; +import { getLocalEntityChildSucc, getLocalEntityFirstChild, overloadEntityListRoot } from "../../system/en_list"; +import { defaultMessageResponse, messageResponses, type MessageResponseFn } from "../../system/en_msgs"; +import { updateClientServerEntity } from "../../system/en_updt"; +import { setLocalEntityIntValue } from "../../system/en_values"; +import type { Entity } from "../../system/entity"; + +// C provenance: up_update.h :: struct UPDATE +export interface UpdateRaw {} + +// C provenance: up_update.c :: entity *update_entity = NULL, *update_succ = NULL; +let update_entity: Entity | undefined = undefined; + +let update_succ: Entity | undefined = undefined; + +export function resetUpdateEntity(): void { + update_entity = undefined; + update_succ = undefined; +} + +// C provenance: up_update.h :: #define get_update_entity() (update_entity) +export function getUpdateEntity(): Entity | undefined { + return update_entity; +} + +// C provenance: up_update.c :: set_update_entity (debug_log not ported) +export function setUpdateEntity(en: Entity | undefined): void { + update_entity = en; +} + +// C provenance: up_update.h :: get_update_succ () / set_update_succ () +export function getUpdateSucc(): Entity | undefined { + return update_succ; +} + +export function setUpdateSucc(en: Entity | undefined): void { + update_succ = en; +} + +// C truncation of a positive or negative double to int +function toCInt(value: number): number { + return value < 0 ? Math.ceil(value) : Math.floor(value); +} + +// C provenance: up_update.c :: set_entity_update_frame_rate +export function setEntityUpdateFrameRate(frame_rate: number): number { + ASSERT(frame_rate >= 1 && frame_rate <= 100, "(frame_rate >= 1) && (frame_rate <= 100)"); + + // (int) (get_delta_time () * frame_rate + 1.0): float * int is float, + 1.0 is double + const entity_update_iterations = toCInt(toFloat32(getDeltaTime() * frame_rate) + 1.0); + + // get_delta_time () / entity_update_iterations: float / int is float + const entity_update_delta_time = toFloat32(getDeltaTime() / entity_update_iterations); + + setManualDeltaTime(entity_update_delta_time); + + return entity_update_iterations; +} + +// C provenance: up_update.c :: update_client_server_entities +export function updateClientServerEntities(): void { + ASSERT(getUpdateEntity() !== undefined, "get_update_entity ()"); + + let iterations = 1; + + let delta_time = 0; + + if (!isFrameRateLocked()) { + delta_time = getDeltaTime(); + + iterations = setEntityUpdateFrameRate(getCommandLineEntityUpdateFrameRate()); + } + + // tacview frame logging: excluded (never logging) + + for (let loop = 0; loop < iterations; loop++) { + let en = getLocalEntityFirstChild(update_entity as Entity, ListType.LIST_TYPE_UPDATE); + + while (en) { + setUpdateSucc(getLocalEntityChildSucc(en, ListType.LIST_TYPE_UPDATE)); + + updateClientServerEntity(en); + + setLocalEntityIntValue(en, IntType.INT_TYPE_UPDATED, 1); + + en = getUpdateSucc(); + } + + setUpdateSucc(undefined); + } + + if (!isFrameRateLocked()) { + setManualDeltaTime(delta_time); + } +} + +// C provenance: up_msgs.c :: response_to_unlink_child +const responseToUnlinkChild: MessageResponseFn = (_message, _receiver, sender, args) => { + const type = args[0] as ListType; + + if (type === ListType.LIST_TYPE_UPDATE) { + if (sender === getUpdateSucc()) { + setUpdateSucc(getLocalEntityChildSucc(sender as Entity, ListType.LIST_TYPE_UPDATE)); + } + } + + return 1; +}; + +export function overloadUpdateFunctions(): void { + const UPDATE = EntityType.ENTITY_TYPE_UPDATE; + + // C provenance: up_list.c :: #define LIST_TYPE_UPDATE_ROOT + overloadEntityListRoot(UPDATE, "update_root", [ListType.LIST_TYPE_UPDATE]); + + // C provenance: up_msgs.c :: overload_update_message_responses. With + // DEBUG_MODULE 0 only UNLINK_CHILD is overloaded; the others keep + // en_msgs.c :: default_message_response. + messageResponses.overload(UPDATE, EntityMessage.ENTITY_MESSAGE_UNLINK_CHILD, responseToUnlinkChild); + messageResponses.overload(UPDATE, EntityMessage.ENTITY_MESSAGE_LINK_CHILD, defaultMessageResponse); + messageResponses.overload(UPDATE, EntityMessage.ENTITY_MESSAGE_LINK_PARENT, defaultMessageResponse); + messageResponses.overload(UPDATE, EntityMessage.ENTITY_MESSAGE_UNLINK_PARENT, defaultMessageResponse); +} diff --git a/eech-core-ts/src/entity/system/en_list.ts b/eech-core-ts/src/entity/system/en_list.ts index 9aa1cff3a..e48aa073e 100644 --- a/eech-core-ts/src/entity/system/en_list.ts +++ b/eech-core-ts/src/entity/system/en_list.ts @@ -13,8 +13,10 @@ // whichever of those lists the group is in. That aliasing is preserved. // -import { ASSERT, UnportedBehaviourError } from "../../core/assert"; +import { ASSERT, EechFatalError, UnportedBehaviourError } from "../../core/assert"; +import { EntityMessage } from "../../generated/c-enums"; import { EntityType, ListType } from "../../generated/c-enums"; +import { notifyLocalEntity } from "./en_msgs"; import type { Entity, EntityListLink, EntityListRoot } from "./entity"; interface ListLayout { @@ -104,6 +106,106 @@ export function getLocalEntityChildSucc(en: Entity, type: ListType): Entity | un return getLink(en, type).child_succ; } +// C provenance: en_list.c :: get_local_entity_child_pred +export function getLocalEntityChildPred(en: Entity, type: ListType): Entity | undefined { + return getLink(en, type).child_pred; +} + +function setLocalEntityFirstChild(en: Entity, type: ListType, first_child: Entity | undefined): void { + getRoot(en, type).first_child = first_child; +} + +function setLocalEntityParent(en: Entity, type: ListType, parent: Entity | undefined): void { + getLink(en, type).parent = parent; +} + +function setLocalEntityChildSucc(en: Entity, type: ListType, child_succ: Entity | undefined): void { + getLink(en, type).child_succ = child_succ; +} + +function setLocalEntityChildPred(en: Entity, type: ListType, child_pred: Entity | undefined): void { + getLink(en, type).child_pred = child_pred; +} + +// +// C provenance: en_list.c :: insert_local_entity_into_parents_child_list +// +// The #ifdef DEBUG list validation is kept, with EECH's debug-build meaning +// (as ASSERT is): inserting an entity into a list it is already in is fatal. +// +export function insertLocalEntityIntoParentsChildList(en: Entity, type: ListType, parent: Entity | undefined, pred: Entity | undefined): void { + ASSERT(parent !== undefined, "parent"); + + ASSERT(pred !== parent, "pred != parent"); + + const current_parent = getLocalEntityParent(en, type); + + if (current_parent) { + let item = getLocalEntityFirstChild(current_parent, type); + + while (item) { + if (item === en) { + throw new EechFatalError(`Entity already in list (entity type = ${EntityType[en.type]}, list type = ${ListType[type]})`); + } + + item = getLocalEntityChildSucc(item, type); + } + } + + const succ = pred ? getLocalEntityChildSucc(pred, type) : getLocalEntityFirstChild(parent, type); + + setLocalEntityChildSucc(en, type, succ); + + setLocalEntityChildPred(en, type, pred); + + setLocalEntityParent(en, type, parent); + + if (succ) { + setLocalEntityChildPred(succ, type, en); + } + + if (pred) { + setLocalEntityChildSucc(pred, type, en); + } else { + setLocalEntityFirstChild(parent, type, en); + } + + notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_LINK_CHILD, parent, en, type); + + notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_LINK_PARENT, en, parent, type); +} + +// C provenance: en_list.c :: delete_local_entity_from_parents_child_list +export function deleteLocalEntityFromParentsChildList(en: Entity, type: ListType): void { + const parent = getLocalEntityParent(en, type); + + if (parent) { + notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_UNLINK_CHILD, parent, en, type); + + notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_UNLINK_PARENT, en, parent, type); + + const succ = getLocalEntityChildSucc(en, type); + + const pred = getLocalEntityChildPred(en, type); + + if (pred) { + setLocalEntityChildSucc(pred, type, succ); + } else { + setLocalEntityFirstChild(parent, type, succ); + } + + if (succ) { + setLocalEntityChildPred(succ, type, pred); + } + + setLocalEntityParent(en, type, undefined); + + setLocalEntityChildSucc(en, type, undefined); + + setLocalEntityChildPred(en, type, undefined); + } +} + // // Establishes list membership exactly as the pointer updates of // en_list.c :: insert_local_entity_into_parents_child_list, but without its diff --git a/eech-core-ts/src/entity/system/en_msgs.ts b/eech-core-ts/src/entity/system/en_msgs.ts index 38baaaf61..cde818a68 100644 --- a/eech-core-ts/src/entity/system/en_msgs.ts +++ b/eech-core-ts/src/entity/system/en_msgs.ts @@ -26,6 +26,12 @@ export function notifyLocalEntity(message: EntityMessage, receiver: Entity | und return messageResponses.lookup(receiver.type, message, EntityMessage[message])(message, receiver, sender, args); } +// C provenance: en_msgs.c :: default_message_response. Only installed where the +// C message table is known to keep this default. +export function defaultMessageResponse(): number { + return 0; +} + // // Marks a message response that EECH overloads but the port has not ported // yet. With the "throw" policy (production) delivery fails loudly. With the diff --git a/eech-core-ts/src/entity/system/en_updt.ts b/eech-core-ts/src/entity/system/en_updt.ts new file mode 100644 index 000000000..af79e1f24 --- /dev/null +++ b/eech-core-ts/src/entity/system/en_updt.ts @@ -0,0 +1,27 @@ +// +// Entity update dispatch. +// +// C provenance: entity/system/en_funcs/en_updt.c, en_updt.h +// +// #define update_client_server_entity(EN) \ +// (fn_update_client_server_entity[get_local_entity_type ((EN))][get_comms_model ()] ((EN))) +// +// EECH's default (default_update_entity) does nothing. It is not installed: +// an entity type whose update overload is not ported fails loudly. +// + +import { CommsModelType } from "../../generated/c-enums"; +import { getCommsModel } from "./comms"; +import type { Entity } from "./entity"; +import { EntityFunctionTable } from "./function-table"; + +export type UpdateFn = (en: Entity) => void; + +// keyed by [entity type][comms model] +export const fnUpdateClientServerEntity = new EntityFunctionTable("fn_update_client_server_entity"); + +export function updateClientServerEntity(en: Entity): void { + const model = getCommsModel(); + + fnUpdateClientServerEntity.lookup(en.type, model, CommsModelType[model])(en); +} diff --git a/eech-core-ts/src/entity/system/en_values.ts b/eech-core-ts/src/entity/system/en_values.ts index 8e595f004..49965a76a 100644 --- a/eech-core-ts/src/entity/system/en_values.ts +++ b/eech-core-ts/src/entity/system/en_values.ts @@ -17,6 +17,7 @@ import { getCampaignPorts, type Entity } from "./entity"; import { EntityFunctionTable } from "./function-table"; export type GetIntValueFn = (en: Entity, type: IntType) => number; +export type SetIntValueFn = (en: Entity, type: IntType, value: number) => void; export type GetFloatValueFn = (en: Entity, type: FloatType) => number; export type SetFloatValueFn = (en: Entity, type: FloatType, value: number) => void; export type GetVec3dPtrFn = (en: Entity, type: Vec3dType) => Vec3d | undefined; @@ -24,6 +25,8 @@ export type GetPtrValueFn = (en: Entity, type: PtrType) => Entity | undefined; export const fnGetLocalEntityIntValue = new EntityFunctionTable("fn_get_local_entity_int_value"); +export const fnSetLocalEntityIntValue = new EntityFunctionTable("fn_set_local_entity_int_value"); + export const fnGetLocalEntityFloatValue = new EntityFunctionTable("fn_get_local_entity_float_value"); export const fnSetClientServerEntityFloatValue: Record> = { @@ -39,6 +42,15 @@ export function getLocalEntityIntValue(en: Entity, type: IntType): number { return fnGetLocalEntityIntValue.lookup(en.type, type, IntType[type])(en, type); } +// The C prototype takes `int value`. +export function setLocalEntityIntValue(en: Entity, type: IntType, value: number): void { + fnSetLocalEntityIntValue.lookup(en.type, type, IntType[type])(en, type, value); +} + +// C provenance: en_int.c :: default_set_entity_int_value (does nothing). Only +// installed where the C overload tables are known to keep this default. +export function defaultSetEntityIntValue(_en: Entity, _type: IntType, _value: number): void {} + export function getLocalEntityFloatValue(en: Entity, type: FloatType): number { return fnGetLocalEntityFloatValue.lookup(en.type, type, FloatType[type])(en, type); } diff --git a/eech-core-ts/src/index.ts b/eech-core-ts/src/index.ts index 3c4643a92..fa7a59053 100644 --- a/eech-core-ts/src/index.ts +++ b/eech-core-ts/src/index.ts @@ -6,6 +6,10 @@ // import type { CampaignPorts } from "./ports"; +import { DEFAULT_ENTITY_UPDATE_FRAME_RATE, setCommandLineEntityUpdateFrameRate } from "./core/cmndline"; +import { resetDeltaTime, setDeltaTimeFrom } from "./core/time"; +import { getCampaignPorts } from "./entity/system/entity"; +import { overloadUpdateFunctions, resetUpdateEntity } from "./entity/special/update/update"; import { initialiseEntityRuntime, type UnportedMessagePolicy } from "./entity/system/entity"; import { overloadMobileFunctions } from "./entity/mobile/mobile"; import { overloadForceFunctions } from "./entity/special/force/force"; @@ -18,21 +22,41 @@ export interface CampaignCoreOptions { // "throw" in production. Test harnesses may use "record" to observe // deliveries to message responses that are not ported yet. unportedMessagePolicy?: UnportedMessagePolicy; + + // EECH.INI "entity update frame rate" (cmndline.c default 2) + entityUpdateFrameRate?: number; } export function initialiseCampaignCore(ports: CampaignPorts, options: CampaignCoreOptions = {}): void { initialiseEntityRuntime(ports, options.unportedMessagePolicy ?? "throw"); + resetDeltaTime(); + resetUpdateEntity(); + setCommandLineEntityUpdateFrameRate(options.entityUpdateFrameRate ?? DEFAULT_ENTITY_UPDATE_FRAME_RATE); + overloadSessionListFunctions(); overloadForceFunctions(); overloadKeysiteFunctions(); overloadGroupFunctions(); overloadGuideFunctions(); overloadMobileFunctions(); + overloadUpdateFunctions(); +} + +// +// Host frame boundary. EECH's flight loop (flight.c) measures the frame +// (set_delta_time) and then calls update_client_server_entities () once per +// time-acceleration step. setDeltaTime () takes the measurement from the Clock +// port; the host decides how many times to call updateClientServerEntities (). +// +export function setDeltaTime(): void { + setDeltaTimeFrom(getCampaignPorts().clock); } -export type { CampaignPorts, EntityReplication, MobilePhysicalState } from "./ports"; +export type { CampaignPorts, Clock, EntityReplication, MobilePhysicalState } from "./ports"; export { assessGroupSupplies } from "./entity/special/group/group"; +export { setUpdateEntity, updateClientServerEntities } from "./entity/special/update/update"; +export { setClientServerEntityFloatValue } from "./entity/system/en_values"; // Campaign state restoration primitives (the state en_pack.c unpacking // establishes). A CampaignStore port will replace these once session loading diff --git a/eech-core-ts/src/ports/clock.ts b/eech-core-ts/src/ports/clock.ts new file mode 100644 index 000000000..09912be60 --- /dev/null +++ b/eech-core-ts/src/ports/clock.ts @@ -0,0 +1,23 @@ +// +// Port: the frame clock. +// +// C provenance: modules/system/time.c :: set_delta_time, lock_frame_rate / unlock_frame_rate +// +// EECH measures each frame with timeGetTime (never less than 0.001 s while +// unlocked) or uses the locked frame rate's fixed delta, and stores the result +// in system_delta_time, which the campaign reads through get_delta_time (). +// Measuring time is the environment's job. The campaign core only needs the +// result, and whether the frame rate is locked, because a locked frame rate +// changes how the entity update loop subdivides the frame +// (up_update.c, set_manual_delta_time). +// +// Values are narrowed to C `float` by the core, not by the adapter. +// + +export interface Clock { + // The frame's delta time in seconds (C: system_delta_time after set_delta_time ()). + getDeltaTime(): number; + + // C: locked_frame_rate + isFrameRateLocked(): boolean; +} diff --git a/eech-core-ts/src/ports/index.ts b/eech-core-ts/src/ports/index.ts index c098b225c..166a4df5a 100644 --- a/eech-core-ts/src/ports/index.ts +++ b/eech-core-ts/src/ports/index.ts @@ -1,6 +1,8 @@ +export type { Clock } from "./clock"; export type { EntityReplication } from "./entity-replication"; export type { MobilePhysicalState } from "./mobile-physical-state"; +import type { Clock } from "./clock"; import type { EntityReplication } from "./entity-replication"; import type { MobilePhysicalState } from "./mobile-physical-state"; @@ -8,4 +10,5 @@ import type { MobilePhysicalState } from "./mobile-physical-state"; export interface CampaignPorts { mobilePhysicalState: MobilePhysicalState; entityReplication: EntityReplication; + clock: Clock; } diff --git a/eech-core-ts/test/adapters/scripted-clock.ts b/eech-core-ts/test/adapters/scripted-clock.ts new file mode 100644 index 000000000..bb7ee7ff1 --- /dev/null +++ b/eech-core-ts/test/adapters/scripted-clock.ts @@ -0,0 +1,25 @@ +// +// Deterministic Clock: the frame driver states each frame's delta and whether +// the frame rate is locked. The adapter never measures or invents time. +// + +import type { Clock } from "../../src/ports"; + +export class ScriptedClock implements Clock { + private deltaTime = 0.1; + + private locked = false; + + public setFrame(deltaTime: number, locked: boolean): void { + this.deltaTime = deltaTime; + this.locked = locked; + } + + public getDeltaTime(): number { + return this.deltaTime; + } + + public isFrameRateLocked(): boolean { + return this.locked; + } +} diff --git a/eech-core-ts/test/c-reference/c-harness.ts b/eech-core-ts/test/c-reference/c-harness.ts index ca6aa9645..2d3a69cb1 100644 --- a/eech-core-ts/test/c-reference/c-harness.ts +++ b/eech-core-ts/test/c-reference/c-harness.ts @@ -8,6 +8,7 @@ import { existsSync } from "node:fs"; // @ts-expect-error plain ESM build script without type declarations import { HARNESS_BINARY } from "../../c-reference/build.mjs"; import { serialiseScenario, type ScenarioOutcome, type ScenarioSpec } from "../scenarios/campaign-scenario"; +import { serialiseTimeline, type TimelineOutcome, type TimelineSpec, type TimelineStepState } from "../scenarios/update-timeline"; // Built once by test/c-reference/global-setup.ts; never rebuilt from a worker. function harness(): string { @@ -17,8 +18,17 @@ function harness(): string { return HARNESS_BINARY as string; } +// A run that did not exit normally with status 0 is never an outcome: in +// particular a process killed by a signal (e.g. a SIGSEGV outside the NULL page, +// which the harness deliberately does not handle) is a harness failure. function describeFailure(run: SpawnSyncReturns): string { - return run.error ? `${run.error.message}` : `exit ${run.status}: ${run.stderr}`; + if (run.error) { + return run.error.message; + } + if (run.signal !== null) { + return `killed by ${run.signal}: ${run.stderr}`; + } + return `exit ${run.status}: ${run.stderr}`; } export function formatNumberForC(n: number): string { @@ -93,3 +103,34 @@ export function runCRange(x1: number, z1: number, x2: number, z2: number): { ran return { range: floatFromBits(w[1]), approx: floatFromBits(w[2]) }; } + +export function runCTimeline(spec: TimelineSpec): TimelineOutcome { + const input = serialiseTimeline(spec, formatNumberForC); + const run = spawnSync(harness(), [], { input, encoding: "utf8" }); + + if (run.status !== 0) { + throw new Error(`C harness failed (${describeFailure(run)})\ninput:\n${input}`); + } + + const outcome: TimelineOutcome = { result: "", transmissions: [], steps: [] }; + let current: TimelineStepState | undefined; + + for (const line of run.stdout.split("\n")) { + const w = line.split(" "); + + if (w[0] === "transmit") { + outcome.transmissions.push({ step: outcome.steps.length, entity: w[1], floatType: Number(w[2]), value: floatFromBits(w[3]) }); + } else if (w[0] === "step") { + current = { delta: floatFromBits(w[2]), updateList: w[3] === "-" ? [] : w[3].split(","), groups: [] }; + outcome.steps.push(current); + } else if (w[0] === "timer") { + current?.groups.push({ sleep: floatFromBits(w[1]), assist: floatFromBits(w[2]) }); + } else if (w[0] === "result") { + outcome.result = w[1] === "assert" ? `assert:${w.slice(2).join(" ")}` : w[1]; + } else if (line !== "") { + throw new Error(`unexpected C harness output: ${line}`); + } + } + + return outcome; +} diff --git a/eech-core-ts/test/c-reference/differential.cref.test.ts b/eech-core-ts/test/c-reference/differential.cref.test.ts index 42c301578..8d5c82ab0 100644 --- a/eech-core-ts/test/c-reference/differential.cref.test.ts +++ b/eech-core-ts/test/c-reference/differential.cref.test.ts @@ -13,12 +13,17 @@ import { get2dRange, getApprox2dRange } from "../../src/core/maths/range"; import { toFloat32 } from "../../src/core/float32"; import { runScenario, type ScenarioOutcome } from "../scenarios/campaign-scenario"; import { C_REFERENCE_RANDOM_CASES } from "../scenarios/generated/c-reference-random.cases"; -import { runCRange, runCScenario } from "./c-harness"; -import { generateRandomScenarios, mulberry32 } from "./random-scenarios"; +import { C_REFERENCE_RANDOM_TIMELINES } from "../scenarios/generated/c-reference-random-timelines.cases"; +import { runTimeline, type TimelineOutcome } from "../scenarios/update-timeline"; +import { runCRange, runCScenario, runCTimeline } from "./c-harness"; +import { generateRandomScenarios, generateRandomTimelines, mulberry32 } from "./random-scenarios"; const RECORDED_SEED = 20260922; const RECORDED_COUNT = 250; +const RECORDED_TIMELINE_SEED = 20260923; +const RECORDED_TIMELINE_COUNT = 150; + function assertNoNegativeZero(value: unknown, path: string): void { if (typeof value === "number" && Object.is(value, -0)) { throw new Error(`${path} is -0, which the recorded fixture cannot represent`); @@ -51,6 +56,25 @@ describe("C reference: recorded random scenarios", () => { ].join("\n"); writeFileSync(join(__dirname, "../scenarios/generated/c-reference-random.cases.ts"), text); }); + it("records the timeline fixture from the original C", () => { + const cases = generateRandomTimelines(RECORDED_TIMELINE_SEED, RECORDED_TIMELINE_COUNT).map((spec, i) => { + const expected: TimelineOutcome = runCTimeline(spec); + assertNoNegativeZero(expected, `timeline ${i}`); + return { id: `timeline-${RECORDED_TIMELINE_SEED}-${i}`, c: "random timeline, outcome recorded from the original C", spec, expected }; + }); + const text = [ + "//", + "// GENERATED by `npm run cref:record` from the executed original EECH C", + `// (c-reference/harness.c timeline mode, seed ${RECORDED_TIMELINE_SEED}). Do not edit.`, + "//", + "", + 'import type { TimelineCase } from "../update-timeline.cases";', + "", + `export const C_REFERENCE_RANDOM_TIMELINES: TimelineCase[] = ${JSON.stringify(cases, null, "\t")};`, + "", + ].join("\n"); + writeFileSync(join(__dirname, "../scenarios/generated/c-reference-random-timelines.cases.ts"), text); + }); return; } @@ -63,6 +87,16 @@ describe("C reference: recorded random scenarios", () => { expect(runCScenario(c.spec), c.id).toEqual(c.expected); } }); + + it("the timeline fixture is non-trivial", () => { + expect(C_REFERENCE_RANDOM_TIMELINES.length).toBe(RECORDED_TIMELINE_COUNT); + }); + + it("every recorded timeline is still what the original C produces", () => { + for (const c of C_REFERENCE_RANDOM_TIMELINES) { + expect(runCTimeline(c.spec), c.id).toEqual(c.expected); + } + }); }); describe("C reference: live differential", () => { @@ -85,6 +119,32 @@ describe("C reference: live differential", () => { expect(kinds).toContain("closest:assert:v2"); }); + it("TypeScript matches the original C on 1000 fresh random update timelines", () => { + const reached: Record = {}; + for (const spec of generateRandomTimelines(0x7153, 1000)) { + const c = runCTimeline(spec); + expect(runTimeline(spec)).toEqual(c); + reached[c.result] = true; + for (let i = 0; i < c.steps.length; i++) { + const before = i === 0 ? undefined : c.steps[i - 1]; + const step = spec.steps[i]; + if (step.kind === "frame" && step.count > 0 && before !== undefined && before.updateList.length > c.steps[i].updateList.length) { + reached["expiry"] = true; + } + if (step.kind === "frame" && step.count > 0 && !step.locked && step.delta * spec.entityUpdateFrameRate + 1 >= 3) { + reached["subdivided"] = true; + } + if (step.kind === "frame" && step.locked) { + reached["locked"] = true; + } + } + } + // the generator must keep reaching the interesting outcomes + for (const key of ["ok", "assert:(frame_rate >= 1) && (frame_rate <= 100)", "expiry", "subdivided", "locked"]) { + expect(reached[key], key).toBe(true); + } + }); + it("get_2d_range and get_approx_2d_range match the original C bit for bit", () => { const rnd = mulberry32(42); for (let i = 0; i < 400; i++) { diff --git a/eech-core-ts/test/c-reference/harness-faults.cref.test.ts b/eech-core-ts/test/c-reference/harness-faults.cref.test.ts new file mode 100644 index 000000000..fdcb3b8f6 --- /dev/null +++ b/eech-core-ts/test/c-reference/harness-faults.cref.test.ts @@ -0,0 +1,26 @@ +// +// The C reference must report EECH's NULL dereferences as outcomes, but must +// never turn any other crash into a result. +// + +import { describe, expect, it } from "vitest"; +import { ASSESS_GROUP_SUPPLIES_CASES } from "../scenarios/assess-group-supplies.cases"; +import { runCInput, runCScenario } from "./c-harness"; + +describe("C reference harness fault handling", () => { + it("reports a read inside the NULL page as a NULL dereference", () => { + expect(runCInput("op fault-null\n").result).toBe("null-dereference"); + }); + + it("reports the original code's NULL dereference, with the final state", () => { + const c = ASSESS_GROUP_SUPPLIES_CASES.find((x) => x.id === "no-force-for-side-dereferences-null"); + expect(c).toBeDefined(); + const outcome = runCScenario(c!.spec); + expect(outcome.result).toBe("null-dereference"); + expect(outcome).toEqual(c!.expected); + }); + + it("never turns a fault outside the NULL page into an outcome", () => { + expect(() => runCInput("op fault-unmapped\n")).toThrow(/killed by SIGSEGV/); + }); +}); diff --git a/eech-core-ts/test/c-reference/harness-signal-safety.cref.test.ts b/eech-core-ts/test/c-reference/harness-signal-safety.cref.test.ts new file mode 100644 index 000000000..73fbd069f --- /dev/null +++ b/eech-core-ts/test/c-reference/harness-signal-safety.cref.test.ts @@ -0,0 +1,80 @@ +// +// The SIGSEGV handler may only reach write () and _exit (). This is checked on +// the compiled harness object, not by reading the source: the handler's static +// call graph is walked through objdump's disassembly and relocations, so a +// libc call added directly, through a helper, or by the compiler (e.g. +// __stack_chk_fail, an implicit memcpy) fails the test. +// + +import { spawnSync } from "node:child_process"; +import { join } from "node:path"; +import { expect, it } from "vitest"; +// @ts-expect-error plain ESM build script without type declarations +import { HARNESS_BUILD_DIR, HARNESS_OBJECT_NAME } from "../../c-reference/build.mjs"; + +const HANDLER = "segmentation_fault"; +const ALLOWED_EXTERNAL = new Set(["write", "_exit"]); + +function callGraph(objectFile: string): Map> { + const dump = spawnSync("objdump", ["-dr", "--no-show-raw-insn", objectFile], { encoding: "utf8" }); + if (dump.status !== 0) { + throw new Error(`objdump failed: ${dump.error ?? ""}${dump.stderr}`); + } + + const graph = new Map>(); + let current: Set | undefined; + + for (const line of dump.stdout.split("\n")) { + const header = /^[0-9a-f]+ <([^>]+)>:$/.exec(line); + if (header) { + current = new Set(); + graph.set(header[1], current); + continue; + } + if (current === undefined) { + continue; + } + // direct calls and tail jumps to a symbol start (x86-64 call/jmp, arm64 bl/b) + const direct = /\s(?:call|callq|jmp|jmpq|bl|b)\s+[0-9a-f]+ <([^>+]+)>/.exec(line); + if (direct) { + current.add(direct[1]); + } + // relocated calls (x86-64 PLT32, arm64 CALL26 / JUMP26) + const reloc = /R_(?:X86_64_PLT32|AARCH64_CALL26|AARCH64_JUMP26)\s+([^\s+-]+)/.exec(line); + if (reloc) { + current.add(reloc[1]); + } + } + + return graph; +} + +it("the SIGSEGV handler reaches no library call but write () and _exit ()", () => { + const graph = callGraph(join(HARNESS_BUILD_DIR as string, HARNESS_OBJECT_NAME as string)); + + expect(graph.has(HANDLER)).toBe(true); + + const external = new Set(); + const seen = new Set(); + const pending = [HANDLER]; + + while (pending.length > 0) { + const fn = pending.pop() as string; + if (seen.has(fn)) { + continue; + } + seen.add(fn); + for (const target of graph.get(fn) ?? []) { + if (graph.has(target)) { + pending.push(target); + } else { + external.add(target); + } + } + } + + expect([...external].sort()).toEqual([...ALLOWED_EXTERNAL].sort()); + // sanity: the walk really followed the helpers + expect(seen.has("emit_final_state")).toBe(true); + expect(seen.has("write_all")).toBe(true); +}); diff --git a/eech-core-ts/test/c-reference/random-scenarios.ts b/eech-core-ts/test/c-reference/random-scenarios.ts index fdab58164..ce1c66c9b 100644 --- a/eech-core-ts/test/c-reference/random-scenarios.ts +++ b/eech-core-ts/test/c-reference/random-scenarios.ts @@ -4,8 +4,9 @@ // executing the original C. // -import { EntitySide, EntitySubTypeGroup, EntitySubTypeKeysite } from "../../src/generated/c-enums"; +import { EntitySide, EntitySubTypeGroup, EntitySubTypeKeysite, FloatType } from "../../src/generated/c-enums"; import type { GroupParentSpec, KeysiteSpec, PositionSpec, ScenarioSpec } from "../scenarios/campaign-scenario"; +import type { TimelineSpec, TimelineStep } from "../scenarios/update-timeline"; export function mulberry32(seed: number): () => number { let a = seed >>> 0; @@ -85,3 +86,55 @@ export function generateRandomScenarios(seed: number, count: number): ScenarioSp return scenarios; } + +// +// Random update timelines: groups restored with or without timers and +// update-list membership, then a random mix of timer sets and frames. +// +export function generateRandomTimelines(seed: number, count: number): TimelineSpec[] { + const rnd = mulberry32(seed); + const int = (n: number) => Math.floor(rnd() * n); + const chance = (p: number) => rnd() < p; + const real = (lo: number, hi: number) => Math.round((lo + rnd() * (hi - lo)) * 1000) / 1000; + const timer = () => (chance(0.35) ? 0 : chance(0.1) ? real(-2, 0) : real(0, 5)); + + const timelines: TimelineSpec[] = []; + + for (let n = 0; n < count; n++) { + const groups = []; + const numGroups = 1 + int(4); + for (let i = 0; i < numGroups; i++) { + groups.push({ + subType: int(EntitySubTypeGroup.NUM_ENTITY_SUB_TYPE_GROUPS), + side: chance(0.5) ? EntitySide.ENTITY_SIDE_BLUE_FORCE : EntitySide.ENTITY_SIDE_RED_FORCE, + sleep: timer(), + assist: timer(), + onUpdateList: chance(0.5), + }); + } + + const steps: TimelineStep[] = []; + const numSteps = 1 + int(10); + for (let i = 0; i < numSteps; i++) { + if (chance(0.35)) { + steps.push({ + kind: "set", + group: int(numGroups), + floatType: chance(0.5) ? FloatType.FLOAT_TYPE_SLEEP : FloatType.FLOAT_TYPE_ASSIST_TIMER, + value: chance(0.2) ? 0 : chance(0.1) ? real(-3, 0) : real(0, 8), + }); + } else { + steps.push({ + kind: "frame", + delta: chance(0.2) ? [0.04, 0.1, 0.25, 0.5, 1][int(5)] : real(0.001, 3), + locked: chance(0.15), + count: chance(0.1) ? 0 : 1 + int(3), + }); + } + } + + timelines.push({ entityUpdateFrameRate: chance(0.1) ? [0, 101, 1, 100][int(4)] : 1 + int(8), groups, steps }); + } + + return timelines; +} diff --git a/eech-core-ts/test/c-reference/update-timeline.cref.test.ts b/eech-core-ts/test/c-reference/update-timeline.cref.test.ts new file mode 100644 index 000000000..6f3de8f73 --- /dev/null +++ b/eech-core-ts/test/c-reference/update-timeline.cref.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from "vitest"; +import { UPDATE_TIMELINE_CASES } from "../scenarios/update-timeline.cases"; +import { runCTimeline } from "./c-harness"; + +// Every expected timeline outcome must be what the original C does. +describe("update timeline expectations hold for the original EECH C", () => { + for (const c of UPDATE_TIMELINE_CASES) { + it(c.id, () => { + expect(runCTimeline(c.spec)).toEqual(c.expected); + }); + } +}); diff --git a/eech-core-ts/test/lua/conformance.ts b/eech-core-ts/test/lua/conformance.ts index 7dcedf125..ec28eb1be 100644 --- a/eech-core-ts/test/lua/conformance.ts +++ b/eech-core-ts/test/lua/conformance.ts @@ -13,6 +13,9 @@ import { ASSESS_GROUP_SUPPLIES_CASES } from "../scenarios/assess-group-supplies. import { runScenario } from "../scenarios/campaign-scenario"; import { FLOAT32_EDGE_CASES } from "../scenarios/float32.cases"; import { C_REFERENCE_RANDOM_CASES } from "../scenarios/generated/c-reference-random.cases"; +import { C_REFERENCE_RANDOM_TIMELINES } from "../scenarios/generated/c-reference-random-timelines.cases"; +import { UPDATE_TIMELINE_CASES } from "../scenarios/update-timeline.cases"; +import { runTimeline } from "../scenarios/update-timeline"; declare const _VERSION: string; @@ -92,6 +95,14 @@ for (const c of C_REFERENCE_RANDOM_CASES) { check(c.id, runScenario(c.spec), c.expected); } +for (const c of UPDATE_TIMELINE_CASES) { + check(c.id, runTimeline(c.spec), c.expected); +} + +for (const c of C_REFERENCE_RANDOM_TIMELINES) { + check(c.id, runTimeline(c.spec), c.expected); +} + print(`${_VERSION}: ${passes} passed, ${failures} failed`); os.exit(failures === 0 ? 0 : 1); diff --git a/eech-core-ts/test/lua/smoke.lua b/eech-core-ts/test/lua/smoke.lua index c656c4625..9b8a8f6b5 100644 --- a/eech-core-ts/test/lua/smoke.lua +++ b/eech-core-ts/test/lua/smoke.lua @@ -14,6 +14,10 @@ core.initialiseCampaignCore({ mobilePhysicalState = { getMobilePosition = function(entityIndex) return { x = 0, y = 0, z = 0 } end, }, + clock = { + getDeltaTime = function() return 0.25 end, + isFrameRateLocked = function() return false end, + }, entityReplication = { transmitEntityFloatValue = function(entityIndex, floatType, value) transmitted[#transmitted + 1] = { entityIndex = entityIndex, floatType = floatType, value = value } @@ -42,6 +46,8 @@ local group_raw = { sub_type = core.EntitySubTypeGroup.ENTITY_SUB_TYPE_GROUP_ATTACK_HELICOPTER, side = core.EntitySide.ENTITY_SIDE_BLUE_FORCE, supplies = { ammo_supply_level = 30, fuel_supply_level = 40 }, + sleep = 0, + assist_timer = 0, } local group = core.createLocalEntityRaw(T.ENTITY_TYPE_GROUP, group_raw) core.insertLocalEntityIntoParentsChildListRaw(group, L.LIST_TYPE_KEYSITE_GROUP, keysite, nil) @@ -53,6 +59,17 @@ assert(group_raw.supplies.ammo_supply_level == 80, "ammo " .. group_raw.supplies assert(group_raw.supplies.fuel_supply_level == 90, "fuel " .. group_raw.supplies.fuel_supply_level) assert(#transmitted == 4, "transmissions " .. #transmitted) +-- update timing: a sleeping group counts down each frame and leaves the update list when both timers expire +local update = core.createLocalEntityRaw(T.ENTITY_TYPE_UPDATE, {}) +core.setUpdateEntity(update) +core.setClientServerEntityFloatValue(group, core.FloatType.FLOAT_TYPE_SLEEP, 0.5) +core.setDeltaTime() +core.updateClientServerEntities() +assert(group_raw.sleep == 0.25, "sleep " .. group_raw.sleep) +core.updateClientServerEntities() +assert(group_raw.sleep == 0, "sleep " .. group_raw.sleep) +assert(group.links.update_link.parent == nil, "group should have left the update list") + -- production policy: reaching an unported message response fails loudly group_raw.sub_type = core.EntitySubTypeGroup.ENTITY_SUB_TYPE_GROUP_PRIMARY_FRONTLINE local ok, err = pcall(core.assessGroupSupplies, group) diff --git a/eech-core-ts/test/scenarios/campaign-scenario.ts b/eech-core-ts/test/scenarios/campaign-scenario.ts index 6c03f529e..5538d5b9a 100644 --- a/eech-core-ts/test/scenarios/campaign-scenario.ts +++ b/eech-core-ts/test/scenarios/campaign-scenario.ts @@ -25,10 +25,10 @@ import { getClosestKeysite, type KeysiteRaw } from "../../src/entity/special/key import type { ForceRaw } from "../../src/entity/special/force/force"; import { insertLocalEntityIntoParentsChildListRaw } from "../../src/entity/system/en_list"; import { createLocalEntityRaw, setSessionEntityRaw, takeUnportedMessageLog, type Entity } from "../../src/entity/system/entity"; -import { GROUP_DATABASE_RESUPPLY_SOURCE } from "../../src/generated/c-group-database"; import { EntitySide, EntitySubTypeGroup, EntitySubTypeKeysite, EntityType, ListType } from "../../src/generated/c-enums"; import { InMemoryMobilePhysicalState } from "../adapters/in-memory-mobile-physical-state"; import { RecordingEntityReplication } from "../adapters/recording-entity-replication"; +import { ScriptedClock } from "../adapters/scripted-clock"; export interface KeysiteSpec { side: EntitySide; @@ -125,7 +125,7 @@ export function runScenario(spec: ScenarioSpec): ScenarioOutcome { const physical = new InMemoryMobilePhysicalState(); const replication = new RecordingEntityReplication(); - initialiseCampaignCore({ mobilePhysicalState: physical, entityReplication: replication }, { unportedMessagePolicy: "record" }); + initialiseCampaignCore({ mobilePhysicalState: physical, entityReplication: replication, clock: new ScriptedClock() }, { unportedMessagePolicy: "record" }); const labels: Record = {}; @@ -188,6 +188,8 @@ export function runScenario(spec: ScenarioSpec): ScenarioOutcome { sub_type: g.subType, side: g.side, supplies: { ammo_supply_level: toFloat32(g.ammo), fuel_supply_level: toFloat32(g.fuel) }, + sleep: 0, + assist_timer: 0, }; group = createLocalEntityRaw(EntityType.ENTITY_TYPE_GROUP, groupRaw); labels[group.index] = "group"; @@ -312,7 +314,7 @@ export function serialiseScenario(spec: ScenarioSpec, formatNumber: (n: number) const leader = g.leader.kind === "none" ? "0 0 0" : `1 ${formatNumber(g.leader.x)} ${formatNumber(g.leader.z)}`; lines.push( - `group ${g.subType} ${GROUP_DATABASE_RESUPPLY_SOURCE[g.subType]} ${g.side} ${formatNumber(g.ammo)} ${formatNumber(g.fuel)} ${parentKind} ${parentIndex} ${g.busy ? 1 : 0} ${leader}`, + `group ${g.subType} ${g.side} ${formatNumber(g.ammo)} ${formatNumber(g.fuel)} ${parentKind} ${parentIndex} ${g.busy ? 1 : 0} ${leader}`, ); } diff --git a/eech-core-ts/test/scenarios/generated/c-reference-random-timelines.cases.ts b/eech-core-ts/test/scenarios/generated/c-reference-random-timelines.cases.ts new file mode 100644 index 000000000..ea95b866b --- /dev/null +++ b/eech-core-ts/test/scenarios/generated/c-reference-random-timelines.cases.ts @@ -0,0 +1,27109 @@ +// +// GENERATED by `npm run cref:record` from the executed original EECH C +// (c-reference/harness.c timeline mode, seed 20260923). Do not edit. +// + +import type { TimelineCase } from "../update-timeline.cases"; + +export const C_REFERENCE_RANDOM_TIMELINES: TimelineCase[] = [ + { + "id": "timeline-20260923-0", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 1, + "side": 2, + "sleep": 4.549, + "assist": 2.078, + "onUpdateList": false + }, + { + "subType": 17, + "side": 2, + "sleep": 3.807, + "assist": 4.685, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.769, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.381, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.867, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 4.686 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 7.532 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 1.328 + }, + { + "kind": "frame", + "delta": 1.57, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 0 + }, + { + "step": 4, + "entity": "group1", + "floatType": 119, + "value": 4.685999870300293 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": 7.5320000648498535 + }, + { + "step": 6, + "entity": "group1", + "floatType": 13, + "value": 1.3279999494552612 + } + ], + "steps": [ + { + "delta": 0.7689999938011169, + "updateList": [], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 3.806999921798706, + "assist": 4.684999942779541 + } + ] + }, + { + "delta": 0.38100001215934753, + "updateList": [], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 3.806999921798706, + "assist": 4.684999942779541 + } + ] + }, + { + "delta": 0.38100001215934753, + "updateList": [], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 0, + "assist": 4.684999942779541 + } + ] + }, + { + "delta": 1.8669999837875366, + "updateList": [], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 0, + "assist": 4.684999942779541 + } + ] + }, + { + "delta": 1.8669999837875366, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 4.685999870300293, + "assist": 4.684999942779541 + } + ] + }, + { + "delta": 1.8669999837875366, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 4.685999870300293, + "assist": 7.5320000648498535 + } + ] + }, + { + "delta": 1.8669999837875366, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 4.685999870300293, + "assist": 1.3279999494552612 + } + ] + }, + { + "delta": 1.5700000524520874, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.548999786376953, + "assist": 2.078000068664551 + }, + { + "sleep": 1.545999526977539, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-1", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 6, + "side": 1, + "sleep": 4.135, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.417, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -2.475 + }, + { + "kind": "frame", + "delta": 1.205, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.156 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.973, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.443, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.035 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": -2.4749999046325684 + }, + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 6.156000137329102 + }, + { + "step": 8, + "entity": "group0", + "floatType": 13, + "value": 7.034999847412109 + } + ], + "steps": [ + { + "delta": 0.4169999957084656, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.884000062942505, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.8840000629425049, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.8840000629425049, + "assist": -2.4749999046325684 + } + ] + }, + { + "delta": 1.2050000429153442, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -2.4749999046325684 + } + ] + }, + { + "delta": 1.2050000429153442, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.156000137329102 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.0360002517700195 + } + ] + }, + { + "delta": 0.9729999899864197, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.1170010566711426 + } + ] + }, + { + "delta": 2.443000078201294, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.443000078201294, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.034999847412109 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-2", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 3, + "side": 1, + "sleep": 0, + "assist": 4.73, + "onUpdateList": true + }, + { + "subType": 21, + "side": 2, + "sleep": 0, + "assist": 2.479, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 6.334 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.598 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.274 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.147, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.246, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.459 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 6.334000110626221 + }, + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 1.5980000495910645 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 6.27400016784668 + }, + { + "step": 8, + "entity": "group0", + "floatType": 119, + "value": 4.459000110626221 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.730000019073486 + }, + { + "sleep": 0, + "assist": 6.334000110626221 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.690000057220459 + }, + { + "sleep": 0, + "assist": 6.294000148773193 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 1.5980000495910645, + "assist": 4.690000057220459 + }, + { + "sleep": 0, + "assist": 6.294000148773193 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0.8480000495910645, + "assist": 3.940000057220459 + }, + { + "sleep": 0, + "assist": 5.544000148773193 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 6.27400016784668, + "assist": 3.940000057220459 + }, + { + "sleep": 0, + "assist": 5.544000148773193 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 6.234000205993652, + "assist": 3.9000000953674316 + }, + { + "sleep": 0, + "assist": 5.504000186920166 + } + ] + }, + { + "delta": 0.1469999998807907, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 6.086999893188477, + "assist": 3.753000259399414 + }, + { + "sleep": 0, + "assist": 5.35699987411499 + } + ] + }, + { + "delta": 2.246000051498413, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.246000051498413, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.459000110626221, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-3", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0.724, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 15, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 14, + "side": 1, + "sleep": 2.683, + "assist": -1.79, + "onUpdateList": true + }, + { + "subType": 4, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.925, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.627, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.84, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0.485 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group3", + "floatType": 13, + "value": 0 + }, + { + "step": 4, + "entity": "group2", + "floatType": 119, + "value": 0.48500001430511475 + } + ], + "steps": [ + { + "delta": 1.9249999523162842, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.7239999771118164, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7899999618530273 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.6269999742507935, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.7239999771118164, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7899999618530273 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8399999737739563, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.7239999771118164, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7899999618530273 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8399999737739563, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.7239999771118164, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7899999618530273 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8399999737739563, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.7239999771118164, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.48500001430511475, + "assist": -1.7899999618530273 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-4", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 7, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 2.888, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.973, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -0.319 + }, + { + "kind": "frame", + "delta": 0.309, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.894 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 4.005 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": -0.3190000057220459 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 3.8940000534057617 + }, + { + "step": 4, + "entity": "group1", + "floatType": 119, + "value": 4.005000114440918 + } + ], + "steps": [ + { + "delta": 0.9729999899864197, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.9729999899864197, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3190000057220459 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.3089999854564667, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3190000057220459 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.3089999854564667, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.8940000534057617, + "assist": -0.3190000057220459 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.3089999854564667, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 3.8940000534057617, + "assist": -0.3190000057220459 + }, + { + "sleep": 4.005000114440918, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-5", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 2.18, + "onUpdateList": false + }, + { + "subType": 15, + "side": 2, + "sleep": 0, + "assist": 2.688, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": -2.448 + }, + { + "kind": "frame", + "delta": 0.142, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.439, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.429, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 13, + "value": -2.447999954223633 + } + ], + "steps": [ + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.180000066757202 + }, + { + "sleep": 0, + "assist": 2.687999963760376 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.180000066757202 + }, + { + "sleep": 0, + "assist": -2.447999954223633 + } + ] + }, + { + "delta": 0.1420000046491623, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.180000066757202 + }, + { + "sleep": 0, + "assist": -2.447999954223633 + } + ] + }, + { + "delta": 2.438999891281128, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.180000066757202 + }, + { + "sleep": 0, + "assist": -2.447999954223633 + } + ] + }, + { + "delta": 2.428999900817871, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.180000066757202 + }, + { + "sleep": 0, + "assist": -2.447999954223633 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-6", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 8, + "side": 2, + "sleep": 3.91, + "assist": 0.727, + "onUpdateList": true + }, + { + "subType": 12, + "side": 2, + "sleep": 0.852, + "assist": 2.855, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.3, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.02, + "locked": true, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.30000001192092896, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 3.309999704360962, + "assist": 0.12700000405311584 + }, + { + "sleep": 0.25200003385543823, + "assist": 2.2549996376037598 + } + ] + }, + { + "delta": 1.0199999809265137, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.2699997425079346, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.21499967575073242 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-7", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 21, + "side": 2, + "sleep": 3.451, + "assist": 1.279, + "onUpdateList": false + }, + { + "subType": 20, + "side": 1, + "sleep": 0, + "assist": 3.55, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.85, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.506, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.207, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.415 + }, + { + "kind": "frame", + "delta": 2.366, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.297, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group1", + "floatType": 119, + "value": 1.4149999618530273 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 2.8499999046325684, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 1.2790000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5059999823570251, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 1.2790000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2070000171661377, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 1.2790000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2070000171661377, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 1.2790000438690186 + }, + { + "sleep": 1.4149999618530273, + "assist": 0 + } + ] + }, + { + "delta": 2.365999937057495, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 1.2790000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.365999937057495, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.296999990940094, + "updateList": [], + "groups": [ + { + "sleep": 3.4509999752044678, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-8", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 25, + "side": 2, + "sleep": 0, + "assist": -0.665, + "onUpdateList": false + }, + { + "subType": 21, + "side": 2, + "sleep": 4.052, + "assist": 1.415, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 7.532 + }, + { + "kind": "frame", + "delta": 1.608, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.47, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.665, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 7.5320000648498535 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6650000214576721 + }, + { + "sleep": 7.5320000648498535, + "assist": 1.4149999618530273 + } + ] + }, + { + "delta": 1.6080000400543213, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6650000214576721 + }, + { + "sleep": 5.924000263214111, + "assist": 0 + } + ] + }, + { + "delta": 0.4699999988079071, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6650000214576721 + }, + { + "sleep": 4.9840006828308105, + "assist": 0 + } + ] + }, + { + "delta": 1.6649999618530273, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6650000214576721 + }, + { + "sleep": 1.6540007591247559, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-9", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 4.828, + "assist": 0.893, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.063, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.062999963760376, + "updateList": [], + "groups": [ + { + "sleep": 4.828000068664551, + "assist": 0.8930000066757202 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-10", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 9, + "side": 1, + "sleep": 0, + "assist": 3.026, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.717, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.637 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.742 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.697 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 1.6369999647140503 + }, + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 0.7419999837875366 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 5.697000026702881 + } + ], + "steps": [ + { + "delta": 2.7170000076293945, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.3089999854564667 + } + ] + }, + { + "delta": 2.7170000076293945, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.6369999647140503, + "assist": 0.3089999854564667 + } + ] + }, + { + "delta": 2.7170000076293945, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.7419999837875366, + "assist": 0.3089999854564667 + } + ] + }, + { + "delta": 2.7170000076293945, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.7419999837875366, + "assist": 5.697000026702881 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-11", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 10, + "side": 2, + "sleep": 4.953, + "assist": 4.951, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 2.437, + "assist": 3.439, + "onUpdateList": true + }, + { + "subType": 19, + "side": 1, + "sleep": 2.906, + "assist": 2.032, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.691, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.56, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.274, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.544, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.944, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 0 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 4.953000068664551, + "assist": 4.951000213623047 + }, + { + "sleep": 2.437000036239624, + "assist": 3.438999891281128 + }, + { + "sleep": 2.9059998989105225, + "assist": 0 + } + ] + }, + { + "delta": 2.690999984741211, + "updateList": [], + "groups": [ + { + "sleep": 4.953000068664551, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.559999942779541, + "updateList": [], + "groups": [ + { + "sleep": 4.953000068664551, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 4.953000068664551, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.27399998903274536, + "updateList": [], + "groups": [ + { + "sleep": 4.953000068664551, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.27399998903274536, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5440000295639038, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.944000005722046, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.951000213623047 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-12", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 15, + "side": 2, + "sleep": 2.849, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 14, + "side": 1, + "sleep": 4.585, + "assist": 4.639, + "onUpdateList": true + }, + { + "subType": 19, + "side": 2, + "sleep": 0, + "assist": -0.046, + "onUpdateList": true + }, + { + "subType": 21, + "side": 2, + "sleep": 0, + "assist": 3.271, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 5.114 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 5.113999843597412 + }, + { + "step": 2, + "entity": "group3", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.8489999771118164, + "assist": 0 + }, + { + "sleep": 4.585000038146973, + "assist": 4.638999938964844 + }, + { + "sleep": 0, + "assist": 5.113999843597412 + }, + { + "sleep": 0, + "assist": 3.2709999084472656 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.8489999771118164, + "assist": 0 + }, + { + "sleep": 4.585000038146973, + "assist": 4.638999938964844 + }, + { + "sleep": 0, + "assist": 5.113999843597412 + }, + { + "sleep": 0, + "assist": 3.2709999084472656 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.8489999771118164, + "assist": 0 + }, + { + "sleep": 4.585000038146973, + "assist": 4.638999938964844 + }, + { + "sleep": 0, + "assist": 5.113999843597412 + }, + { + "sleep": 0, + "assist": 3.2709999084472656 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-13", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 7, + "side": 1, + "sleep": 4.268, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.94, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.648, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.127, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.9399999976158142, + "updateList": [], + "groups": [ + { + "sleep": 4.26800012588501, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 4.26800012588501, + "assist": 0 + } + ] + }, + { + "delta": 0.6480000019073486, + "updateList": [], + "groups": [ + { + "sleep": 4.26800012588501, + "assist": 0 + } + ] + }, + { + "delta": 2.127000093460083, + "updateList": [], + "groups": [ + { + "sleep": 4.26800012588501, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-14", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 13, + "side": 2, + "sleep": 1.006, + "assist": 1.249, + "onUpdateList": false + }, + { + "subType": 4, + "side": 2, + "sleep": 3.247, + "assist": 3.256, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.541 + }, + { + "kind": "frame", + "delta": 0.734, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 7.37 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.636 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.274, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 3.5409998893737793 + }, + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 7.369999885559082 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 2.635999917984009 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 3.5409998893737793, + "assist": 1.2489999532699585 + }, + { + "sleep": 3.246999979019165, + "assist": 3.25600004196167 + } + ] + }, + { + "delta": 0.734000027179718, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 2.073000431060791, + "assist": 0 + }, + { + "sleep": 1.7790004014968872, + "assist": 1.788000464439392 + } + ] + }, + { + "delta": 0.734000027179718, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 7.369999885559082, + "assist": 0 + }, + { + "sleep": 1.7790004014968872, + "assist": 1.788000464439392 + } + ] + }, + { + "delta": 0.734000027179718, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 2.635999917984009, + "assist": 0 + }, + { + "sleep": 1.7790004014968872, + "assist": 1.788000464439392 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.6359999179840088, + "assist": 0 + }, + { + "sleep": 0.7790004014968872, + "assist": 0.7880004644393921 + } + ] + }, + { + "delta": 1.2740000486373901, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-15", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 24, + "side": 2, + "sleep": 4.217, + "assist": -1.58, + "onUpdateList": false + }, + { + "subType": 0, + "side": 2, + "sleep": 1.684, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 3, + "side": 1, + "sleep": 2.586, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.457 + }, + { + "kind": "frame", + "delta": 2.44, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.004, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.529 + }, + { + "kind": "frame", + "delta": 2.895, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 0 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 4.456999778747559 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 5.5289998054504395 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 4.2170000076293945, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.5859999656677246, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 4.456999778747559, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.5859999656677246, + "assist": 0 + } + ] + }, + { + "delta": 2.440000057220459, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.004000000189989805, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.004000000189989805, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.5289998054504395, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.8949999809265137, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.5289998054504395, + "assist": -1.5800000429153442 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-16", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 23, + "side": 2, + "sleep": 4.347, + "assist": 1.612, + "onUpdateList": false + }, + { + "subType": 19, + "side": 1, + "sleep": 1.288, + "assist": -0.246, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.303, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.862, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.163 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 4.163000106811523 + } + ], + "steps": [ + { + "delta": 0.30300000309944153, + "updateList": [], + "groups": [ + { + "sleep": 4.3470001220703125, + "assist": 1.6119999885559082 + }, + { + "sleep": 1.2879999876022339, + "assist": -0.2460000067949295 + } + ] + }, + { + "delta": 1.8619999885559082, + "updateList": [], + "groups": [ + { + "sleep": 4.3470001220703125, + "assist": 1.6119999885559082 + }, + { + "sleep": 1.2879999876022339, + "assist": -0.2460000067949295 + } + ] + }, + { + "delta": 1.8619999885559082, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.163000106811523, + "assist": 1.6119999885559082 + }, + { + "sleep": 1.2879999876022339, + "assist": -0.2460000067949295 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-17", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 16, + "side": 1, + "sleep": 0.438, + "assist": 0.863, + "onUpdateList": false + }, + { + "subType": 9, + "side": 1, + "sleep": 0.239, + "assist": 1.26, + "onUpdateList": true + }, + { + "subType": 23, + "side": 1, + "sleep": 0, + "assist": -0.002, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.429, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.772, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 1.995 + }, + { + "kind": "frame", + "delta": 2.982, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.352, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 5.676 + }, + { + "kind": "frame", + "delta": 0.085, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group1", + "floatType": 13, + "value": 1.9950000047683716 + }, + { + "step": 6, + "entity": "group2", + "floatType": 13, + "value": 5.676000118255615 + } + ], + "steps": [ + { + "delta": 0.42899999022483826, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0.831000030040741 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 1.7719999551773071, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 1.7719999551773071, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 1.9950000047683716 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 2.9820001125335693, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 1.9950000047683716 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 1.3519999980926514, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.0020000000949949026 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.676000118255615 + } + ] + }, + { + "delta": 0.08500000089406967, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0.43799999356269836, + "assist": 0.8629999756813049 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.421000003814697 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-18", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 10, + "side": 1, + "sleep": 4.402, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 22, + "side": 1, + "sleep": 0.356, + "assist": 2.434, + "onUpdateList": true + }, + { + "subType": 25, + "side": 1, + "sleep": 3.394, + "assist": 4.516, + "onUpdateList": true + }, + { + "subType": 22, + "side": 2, + "sleep": 1.897, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 7.813 + }, + { + "kind": "frame", + "delta": 0.544, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 119, + "value": 7.813000202178955 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 4.4019999504089355, + "assist": 0 + }, + { + "sleep": 0.35600000619888306, + "assist": 2.434000015258789 + }, + { + "sleep": 7.813000202178955, + "assist": 4.515999794006348 + }, + { + "sleep": 1.8969999551773071, + "assist": 0 + } + ] + }, + { + "delta": 0.5440000295639038, + "updateList": [ + "group1", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 4.4019999504089355, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.8020001649856567 + }, + { + "sleep": 6.1809983253479, + "assist": 2.8839995861053467 + }, + { + "sleep": 0.2649999260902405, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-19", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 3, + "side": 1, + "sleep": 0, + "assist": 0.873, + "onUpdateList": true + }, + { + "subType": 8, + "side": 2, + "sleep": 0.134, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 5, + "side": 2, + "sleep": 2.322, + "assist": 3.082, + "onUpdateList": true + }, + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 0.998, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.04, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -2.872 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0.211 + }, + { + "kind": "frame", + "delta": 2.85, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.842, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": -2.871999979019165 + }, + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": 0.210999995470047 + } + ], + "steps": [ + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.7529999613761902 + }, + { + "sleep": 0.014000006020069122, + "assist": 0 + }, + { + "sleep": 2.202000141143799, + "assist": 2.9620001316070557 + }, + { + "sleep": 0, + "assist": 0.8779999613761902 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.2529999613761902 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7020001411437988, + "assist": 2.4620001316070557 + }, + { + "sleep": 0, + "assist": 0.3779999613761902 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": -2.871999979019165, + "assist": 0.2529999613761902 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7020001411437988, + "assist": 2.4620001316070557 + }, + { + "sleep": 0, + "assist": 0.3779999613761902 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1", + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": -2.871999979019165, + "assist": 0.2529999613761902 + }, + { + "sleep": 0, + "assist": 0.210999995470047 + }, + { + "sleep": 1.7020001411437988, + "assist": 2.4620001316070557 + }, + { + "sleep": 0, + "assist": 0.3779999613761902 + } + ] + }, + { + "delta": 2.8499999046325684, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -2.871999979019165, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8420000076293945, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -2.871999979019165, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-20", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 25, + "side": 1, + "sleep": 0, + "assist": 1.514, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.347 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.977, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.632 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.751 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.187, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.589 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 0.34700000286102295 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 3.631999969482422 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 3.750999927520752 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": 5.589000225067139 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.34700000286102295 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.34700000286102295 + } + ] + }, + { + "delta": 0.9769999980926514, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.9769999980926514, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.631999969482422 + } + ] + }, + { + "delta": 0.9769999980926514, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.750999927520752, + "assist": 3.631999969482422 + } + ] + }, + { + "delta": 0.9769999980926514, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.750999927520752, + "assist": 0 + } + ] + }, + { + "delta": 0.18700000643730164, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.377000331878662, + "assist": 0 + } + ] + }, + { + "delta": 0.18700000643730164, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.589000225067139, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-21", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 4.451, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -0.582 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.045 + }, + { + "kind": "frame", + "delta": 0.208, + "locked": true, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.404, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.562, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.881, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": -0.5820000171661377 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 2.0450000762939453 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.5820000171661377, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.5820000171661377, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.5820000171661377, + "assist": 2.0450000762939453 + } + ] + }, + { + "delta": 0.20800000429153442, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.5820000171661377, + "assist": 2.0450000762939453 + } + ] + }, + { + "delta": 0.40400001406669617, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.5820000171661377, + "assist": 1.6410000324249268 + } + ] + }, + { + "delta": 0.40400001406669617, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.6410000324249268 + } + ] + }, + { + "delta": 0.5619999766349792, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.0790002346038818 + } + ] + }, + { + "delta": 1.88100004196167, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-22", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 15, + "side": 2, + "sleep": 2.924, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 7, + "side": 2, + "sleep": 2.706, + "assist": -0.432, + "onUpdateList": false + }, + { + "subType": 20, + "side": 1, + "sleep": 2.145, + "assist": 4.839, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.14, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.943, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.1399999856948853, + "updateList": [], + "groups": [ + { + "sleep": 2.9240000247955322, + "assist": 0 + }, + { + "sleep": 2.7060000896453857, + "assist": -0.4320000112056732 + }, + { + "sleep": 2.1449999809265137, + "assist": 4.839000225067139 + } + ] + }, + { + "delta": 1.9429999589920044, + "updateList": [], + "groups": [ + { + "sleep": 2.9240000247955322, + "assist": 0 + }, + { + "sleep": 2.7060000896453857, + "assist": -0.4320000112056732 + }, + { + "sleep": 2.1449999809265137, + "assist": 4.839000225067139 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-23", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 5, + "side": 2, + "sleep": 0.629, + "assist": 0.754, + "onUpdateList": true + }, + { + "subType": 15, + "side": 1, + "sleep": 0, + "assist": 4.697, + "onUpdateList": false + }, + { + "subType": 12, + "side": 2, + "sleep": 0.967, + "assist": 1.179, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.762, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.069, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.267, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.736 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.248 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 4.501 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 7.402 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 4, + "entity": "group1", + "floatType": 119, + "value": 1.7359999418258667 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": 7.248000144958496 + }, + { + "step": 6, + "entity": "group1", + "floatType": 13, + "value": 4.500999927520752 + }, + { + "step": 7, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 8, + "entity": "group2", + "floatType": 119, + "value": 7.4019999504089355 + } + ], + "steps": [ + { + "delta": 1.7619999647140503, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.0689999982714653, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7359999418258667, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.248000144958496 + }, + { + "sleep": 1.7359999418258667, + "assist": 4.697000026702881 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.248000144958496 + }, + { + "sleep": 1.7359999418258667, + "assist": 4.500999927520752 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7359999418258667, + "assist": 4.500999927520752 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2669999599456787, + "updateList": [ + "group2", + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7359999418258667, + "assist": 4.500999927520752 + }, + { + "sleep": 7.4019999504089355, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-24", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 11, + "side": 2, + "sleep": 4.952, + "assist": 0.114, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 1.772, + "assist": 2.529, + "onUpdateList": false + }, + { + "subType": 13, + "side": 1, + "sleep": 4.384, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.101 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.746 + }, + { + "kind": "frame", + "delta": 2.76, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.842, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.79, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 3.408 + }, + { + "kind": "frame", + "delta": 0.742, + "locked": true, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 1.1009999513626099 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0.7459999918937683 + }, + { + "step": 3, + "entity": "group2", + "floatType": 119, + "value": 0 + }, + { + "step": 6, + "entity": "group1", + "floatType": 119, + "value": 3.4079999923706055 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 1.1009999513626099, + "assist": 0.11400000005960464 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 4.383999824523926, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0.7459999918937683, + "assist": 0.11400000005960464 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 4.383999824523926, + "assist": 0 + } + ] + }, + { + "delta": 2.759999990463257, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.759999990463257, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8420000076293945, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.7899999618530273, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7719999551773071, + "assist": 2.5290000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.7899999618530273, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.4079999923706055, + "assist": 2.5290000438690186 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7419999837875366, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.181999921798706, + "assist": 0.3030000925064087 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-25", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 16, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 4, + "side": 1, + "sleep": 3.975, + "assist": 3.849, + "onUpdateList": false + }, + { + "subType": 10, + "side": 1, + "sleep": 0, + "assist": 0.811, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.068, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.619, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 6.227 + }, + { + "kind": "frame", + "delta": 2.311, + "locked": true, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group2", + "floatType": 13, + "value": 6.2270002365112305 + } + ], + "steps": [ + { + "delta": 1.0679999589920044, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.9749999046325684, + "assist": 3.8489999771118164 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.6190000176429749, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.9749999046325684, + "assist": 3.8489999771118164 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.6190000176429749, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.9749999046325684, + "assist": 3.8489999771118164 + }, + { + "sleep": 0, + "assist": 6.2270002365112305 + } + ] + }, + { + "delta": 2.311000108718872, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.9749999046325684, + "assist": 3.8489999771118164 + }, + { + "sleep": 0, + "assist": 6.2270002365112305 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-26", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 101, + "groups": [ + { + "subType": 11, + "side": 2, + "sleep": 0, + "assist": 0.327, + "onUpdateList": true + }, + { + "subType": 2, + "side": 1, + "sleep": 0, + "assist": 0.118, + "onUpdateList": true + }, + { + "subType": 15, + "side": 2, + "sleep": 0, + "assist": 1.689, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 3.337 + }, + { + "kind": "frame", + "delta": 0.197, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.553, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "assert:(frame_rate >= 1) && (frame_rate <= 100)", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 3.3369998931884766 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.3269999921321869 + }, + { + "sleep": 3.3369998931884766, + "assist": 0.11800000071525574 + }, + { + "sleep": 0, + "assist": 1.6890000104904175 + } + ] + }, + { + "delta": 0.19699999690055847, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.3269999921321869 + }, + { + "sleep": 3.3369998931884766, + "assist": 0.11800000071525574 + }, + { + "sleep": 0, + "assist": 1.6890000104904175 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-27", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 3, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.054, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.576, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.857 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0.8569999933242798 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.0540000200271606, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5759999752044678, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5759999752044678, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.8569999933242798 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-28", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 18, + "side": 1, + "sleep": 2.896, + "assist": -1.875, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.567, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.015 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.926, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.232, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.791, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.169, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.4, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.031, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.482 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 0.014999999664723873 + }, + { + "step": 9, + "entity": "group0", + "floatType": 13, + "value": 5.48199987411499 + } + ], + "steps": [ + { + "delta": 0.5669999718666077, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.7619993686676025, + "assist": -1.875 + } + ] + }, + { + "delta": 0.5669999718666077, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.7619993686676025, + "assist": 0.014999999664723873 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.7619993686676025, + "assist": 0 + } + ] + }, + { + "delta": 2.9260001182556152, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2320001125335693, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.7910000085830688, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.1689999103546143, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.4000000953674316, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.0309998989105225, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.0309998989105225, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.48199987411499 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-29", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 9, + "side": 2, + "sleep": 0, + "assist": -0.353, + "onUpdateList": true + }, + { + "subType": 17, + "side": 2, + "sleep": 1.558, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 17, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.438, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 7.405 + }, + { + "kind": "frame", + "delta": 1.087, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.629, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 13, + "value": 7.40500020980835 + } + ], + "steps": [ + { + "delta": 2.437999963760376, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3529999852180481 + }, + { + "sleep": 1.5579999685287476, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.437999963760376, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3529999852180481 + }, + { + "sleep": 1.5579999685287476, + "assist": 7.40500020980835 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.0870000123977661, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3529999852180481 + }, + { + "sleep": 0.47100013494491577, + "assist": 6.318001747131348 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3529999852180481 + }, + { + "sleep": 0.47100013494491577, + "assist": 6.318001747131348 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.628999948501587, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.3529999852180481 + }, + { + "sleep": 0, + "assist": 4.688999176025391 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-30", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 25, + "side": 2, + "sleep": 3.008, + "assist": 3.752, + "onUpdateList": true + }, + { + "subType": 3, + "side": 2, + "sleep": 1.552, + "assist": 3.489, + "onUpdateList": true + }, + { + "subType": 14, + "side": 1, + "sleep": 0, + "assist": 2.318, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.565, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.121, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.505, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.565000057220459, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.007999897003174, + "assist": 3.752000093460083 + }, + { + "sleep": 1.5520000457763672, + "assist": 3.489000082015991 + }, + { + "sleep": 0, + "assist": 2.318000078201294 + } + ] + }, + { + "delta": 1.121000051498413, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0.7659993171691895, + "assist": 1.5099995136260986 + }, + { + "sleep": 0, + "assist": 1.2469995021820068 + }, + { + "sleep": 0, + "assist": 0.07599961757659912 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.6859992742538452, + "assist": 1.429999589920044 + }, + { + "sleep": 0, + "assist": 1.1669995784759521 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5049999952316284, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-31", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 6, + "side": 1, + "sleep": -1.171, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 1.513, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -1.1710000038146973, + "assist": 0 + }, + { + "sleep": 1.5130000114440918, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-32", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 17, + "side": 1, + "sleep": 2.536, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 12, + "side": 1, + "sleep": 0.691, + "assist": 2.828, + "onUpdateList": true + }, + { + "subType": 16, + "side": 2, + "sleep": 0.292, + "assist": 4.606, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.334, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.819, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.985, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.494, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.611, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.918, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.043, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.3340001106262207, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 0.8190000057220459, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 2.984999895095825, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 1.49399995803833, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 2.6110000610351562, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 0.9179999828338623, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + }, + { + "delta": 1.0429999828338623, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2919999957084656, + "assist": 4.605999946594238 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-33", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 4.365, + "onUpdateList": false + }, + { + "subType": 7, + "side": 2, + "sleep": 2.056, + "assist": 2.964, + "onUpdateList": false + }, + { + "subType": 4, + "side": 1, + "sleep": 0, + "assist": 2.201, + "onUpdateList": false + }, + { + "subType": 4, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.539, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.687, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": -2.339 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 2.934 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 7.157 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": -2.3389999866485596 + }, + { + "step": 4, + "entity": "group1", + "floatType": 13, + "value": 2.934000015258789 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": 7.1570000648498535 + } + ], + "steps": [ + { + "delta": 0.5389999747276306, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": 2.9639999866485596 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.687000036239624, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": 2.9639999866485596 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": 2.9639999866485596 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": -2.3389999866485596 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": 2.934000015258789 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.364999771118164 + }, + { + "sleep": 2.055999994277954, + "assist": 7.1570000648498535 + }, + { + "sleep": 0, + "assist": 2.2009999752044678 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-34", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 9, + "side": 1, + "sleep": 4.85, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 23, + "side": 1, + "sleep": 0.909, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.173, + "locked": true, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.1729999780654907, + "updateList": [], + "groups": [ + { + "sleep": 4.849999904632568, + "assist": 0 + }, + { + "sleep": 0.9089999794960022, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-35", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 22, + "side": 1, + "sleep": -0.881, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 2.834, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.321, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 5.675 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.173 + }, + { + "kind": "frame", + "delta": 0.346, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.143, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.905, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.268, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.163, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 5.061 + }, + { + "kind": "frame", + "delta": 2.287, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 13, + "value": 5.675000190734863 + }, + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 4.172999858856201 + }, + { + "step": 8, + "entity": "group1", + "floatType": 13, + "value": 5.060999870300293 + } + ], + "steps": [ + { + "delta": 2.321000099182129, + "updateList": [], + "groups": [ + { + "sleep": -0.8809999823570251, + "assist": 0 + }, + { + "sleep": 2.8340001106262207, + "assist": 0 + } + ] + }, + { + "delta": 2.321000099182129, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": -0.8809999823570251, + "assist": 0 + }, + { + "sleep": 2.8340001106262207, + "assist": 5.675000190734863 + } + ] + }, + { + "delta": 2.321000099182129, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.172999858856201, + "assist": 0 + }, + { + "sleep": 2.8340001106262207, + "assist": 5.675000190734863 + } + ] + }, + { + "delta": 0.34599998593330383, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 3.8269996643066406, + "assist": 0 + }, + { + "sleep": 2.48799991607666, + "assist": 5.329000473022461 + } + ] + }, + { + "delta": 2.1429998874664307, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.0430011749267578 + } + ] + }, + { + "delta": 1.9049999713897705, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2680000066757202, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.16300000250339508, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.16300000250339508, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.060999870300293 + } + ] + }, + { + "delta": 2.2869999408721924, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-36", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 101, + "groups": [ + { + "subType": 9, + "side": 2, + "sleep": 3.287, + "assist": 2.98, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.391, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.389 + }, + { + "kind": "frame", + "delta": 0.819, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "assert:(frame_rate >= 1) && (frame_rate <= 100)", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 2.3889999389648438 + } + ], + "steps": [ + { + "delta": 1.3910000324249268, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.8959999084472656, + "assist": 1.5889999866485596 + } + ] + }, + { + "delta": 1.3910000324249268, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.8959999084472656, + "assist": 2.3889999389648438 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-37", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 2, + "side": 1, + "sleep": 4.44, + "assist": 3.88, + "onUpdateList": false + }, + { + "subType": 24, + "side": 1, + "sleep": 2.576, + "assist": 3.661, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.085, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": -0.818 + }, + { + "kind": "frame", + "delta": 0.173, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.136, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.393, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.051, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group1", + "floatType": 13, + "value": 0 + }, + { + "step": 4, + "entity": "group1", + "floatType": 13, + "value": -0.8180000185966492 + } + ], + "steps": [ + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.5759999752044678, + "assist": 3.6610000133514404 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.5759999752044678, + "assist": 3.6610000133514404 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.5759999752044678, + "assist": 0 + } + ] + }, + { + "delta": 2.0850000381469727, + "updateList": [], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.5759999752044678, + "assist": 0 + } + ] + }, + { + "delta": 2.0850000381469727, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.5759999752044678, + "assist": -0.8180000185966492 + } + ] + }, + { + "delta": 0.17299999296665192, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 2.4029998779296875, + "assist": -0.8180000185966492 + } + ] + }, + { + "delta": 2.135999917984009, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 0.26699984073638916, + "assist": -0.8180000185966492 + } + ] + }, + { + "delta": 0.3930000066757202, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 0, + "assist": -0.8180000185966492 + } + ] + }, + { + "delta": 2.0510001182556152, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440000057220459, + "assist": 3.880000114440918 + }, + { + "sleep": 0, + "assist": -0.8180000185966492 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-38", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 15, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.481, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.151 + }, + { + "kind": "frame", + "delta": 2.263, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.31, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.014 + }, + { + "kind": "frame", + "delta": 0.097, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 3.1510000228881836 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 4.013999938964844 + } + ], + "steps": [ + { + "delta": 0.5, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4809999465942383, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4809999465942383, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.1510000228881836, + "assist": 0 + } + ] + }, + { + "delta": 2.263000011444092, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.3100000023841858, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.3100000023841858, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.013999938964844, + "assist": 0 + } + ] + }, + { + "delta": 0.09700000286102295, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.9169998168945312, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-39", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 0.338, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 0.586, + "assist": -0.409, + "onUpdateList": false + }, + { + "subType": 24, + "side": 1, + "sleep": 1.63, + "assist": 2.782, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.121, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.121000051498413, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.5860000252723694, + "assist": -0.4090000092983246 + }, + { + "sleep": 1.6299999952316284, + "assist": 2.7820000648498535 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-40", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 19, + "side": 2, + "sleep": 0, + "assist": -1.048, + "onUpdateList": false + }, + { + "subType": 12, + "side": 2, + "sleep": 0, + "assist": -1.666, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.728 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 6.728000164031982 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 6.728000164031982, + "assist": -1.0479999780654907 + }, + { + "sleep": 0, + "assist": -1.6660000085830688 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-41", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 14, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 9, + "side": 2, + "sleep": 0, + "assist": 3.673, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.723, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.784, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.771, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.695, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.7230000495910645, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.6730000972747803 + } + ] + }, + { + "delta": 1.784000039100647, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.6730000972747803 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.6730000972747803 + } + ] + }, + { + "delta": 1.7710000276565552, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.6730000972747803 + } + ] + }, + { + "delta": 0.6949999928474426, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.6730000972747803 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-42", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 0, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 8, + "side": 2, + "sleep": 0, + "assist": 2.477, + "onUpdateList": false + }, + { + "subType": 11, + "side": 2, + "sleep": 3.548, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 8, + "side": 2, + "sleep": 4.999, + "assist": 3.527, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.76, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.495, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 6.747 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.417, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.514, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group2", + "floatType": 13, + "value": 6.747000217437744 + }, + { + "step": 6, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 2.759999990463257, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.4950000047683716, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.4950000047683716, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 6.747000217437744 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 6.707000255584717 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.4169999957084656, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 5.873000621795654 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.5139999985694885, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 4.331000804901123 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + }, + { + "delta": 0.5139999985694885, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.4769999980926514 + }, + { + "sleep": 0, + "assist": 4.331000804901123 + }, + { + "sleep": 4.999000072479248, + "assist": 3.5269999504089355 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-43", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 11, + "side": 2, + "sleep": 3.828, + "assist": 3.399, + "onUpdateList": true + }, + { + "subType": 5, + "side": 2, + "sleep": 1.561, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 17, + "side": 2, + "sleep": 0, + "assist": 2.78, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 1.251 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 1.2510000467300415 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.828000068664551, + "assist": 3.3989999294281006 + }, + { + "sleep": 1.5609999895095825, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.2510000467300415 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-44", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 23, + "side": 1, + "sleep": -0.37, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 2, + "side": 1, + "sleep": 4.553, + "assist": -1.644, + "onUpdateList": false + }, + { + "subType": 24, + "side": 2, + "sleep": 0, + "assist": 0.86, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.956, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.9559999704360962, + "updateList": [], + "groups": [ + { + "sleep": -0.3700000047683716, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.552999973297119, + "assist": -1.6440000534057617 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-45", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 9, + "side": 1, + "sleep": 2.416, + "assist": 3.564, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 1.869 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.944 + }, + { + "kind": "frame", + "delta": 0.408, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.05, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.806 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 1.86899995803833 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 6.943999767303467 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 6, + "entity": "group0", + "floatType": 13, + "value": 2.805999994277954 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.4159998893737793, + "assist": 1.86899995803833 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.943999767303467, + "assist": 1.86899995803833 + } + ] + }, + { + "delta": 0.40799999237060547, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.1279988288879395, + "assist": 1.05299973487854 + } + ] + }, + { + "delta": 0.40799999237060547, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.1279988288879395, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.6279988288879395, + "assist": 0 + } + ] + }, + { + "delta": 0.05000000074505806, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.577998638153076, + "assist": 0 + } + ] + }, + { + "delta": 0.05000000074505806, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.577998638153076, + "assist": 2.805999994277954 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-46", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 11, + "side": 2, + "sleep": 1.572, + "assist": 2.928, + "onUpdateList": false + }, + { + "subType": 16, + "side": 2, + "sleep": 2.429, + "assist": 0.243, + "onUpdateList": true + }, + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.758, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0.637 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0.847 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 7.562 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.144, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 119, + "value": 0.6370000243186951 + }, + { + "step": 2, + "entity": "group2", + "floatType": 13, + "value": 0.847000002861023 + }, + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": 7.561999797821045 + } + ], + "steps": [ + { + "delta": 2.757999897003174, + "updateList": [], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.757999897003174, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.6370000243186951, + "assist": 0 + } + ] + }, + { + "delta": 2.757999897003174, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.6370000243186951, + "assist": 0.847000002861023 + } + ] + }, + { + "delta": 2.757999897003174, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 7.561999797821045 + }, + { + "sleep": 0.6370000243186951, + "assist": 0.847000002861023 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 7.441999912261963 + }, + { + "sleep": 0.5169999599456787, + "assist": 0.7269999384880066 + } + ] + }, + { + "delta": 2.1440000534057617, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 1.5720000267028809, + "assist": 2.927999973297119 + }, + { + "sleep": 0, + "assist": 3.1539969444274902 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-47", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 17, + "side": 2, + "sleep": -0.515, + "assist": 4.943, + "onUpdateList": true + }, + { + "subType": 5, + "side": 1, + "sleep": -0.544, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 1, + "side": 1, + "sleep": 3.833, + "assist": 4.079, + "onUpdateList": false + }, + { + "subType": 17, + "side": 1, + "sleep": 2.172, + "assist": 2.715, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.54, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.094, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.969, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.484, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.267, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.517, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.007, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.916, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 6, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 1.5399999618530273, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0.3229992091655731 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.09399999678134918, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0.040999218821525574 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.968999981880188, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.4839999675750732, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2669999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5169999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5169999599456787, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.006999969482422, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.9160000085830688, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.5149999856948853, + "assist": 0 + }, + { + "sleep": -0.5440000295639038, + "assist": 0 + }, + { + "sleep": 3.8329999446868896, + "assist": 4.078999996185303 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-48", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 16, + "side": 1, + "sleep": 0, + "assist": 2.508, + "onUpdateList": false + }, + { + "subType": 5, + "side": 1, + "sleep": 1.78, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 19, + "side": 1, + "sleep": 0, + "assist": 4.73, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.689, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.918, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.612, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.715 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group1", + "floatType": 119, + "value": 2.7149999141693115 + } + ], + "steps": [ + { + "delta": 2.688999891281128, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.507999897003174 + }, + { + "sleep": 1.7799999713897705, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.730000019073486 + } + ] + }, + { + "delta": 1.9179999828338623, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.507999897003174 + }, + { + "sleep": 1.7799999713897705, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.730000019073486 + } + ] + }, + { + "delta": 0.6119999885559082, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.507999897003174 + }, + { + "sleep": 1.7799999713897705, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.730000019073486 + } + ] + }, + { + "delta": 0.6119999885559082, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.507999897003174 + }, + { + "sleep": 2.7149999141693115, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.730000019073486 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-49", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 1, + "side": 2, + "sleep": 0, + "assist": 1.395, + "onUpdateList": false + }, + { + "subType": 10, + "side": 1, + "sleep": 0.191, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.806, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.805999994277954, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.3949999809265137 + }, + { + "sleep": 0.19099999964237213, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.3949999809265137 + }, + { + "sleep": 0.19099999964237213, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-50", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 16, + "side": 1, + "sleep": 0.493, + "assist": 2.353, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.741, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.222, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -1.758 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.601 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.813 + }, + { + "kind": "frame", + "delta": 1.257, + "locked": true, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": -1.7580000162124634 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 0.6010000109672546 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 6.813000202178955 + } + ], + "steps": [ + { + "delta": 0.7409999966621399, + "updateList": [], + "groups": [ + { + "sleep": 0.49300000071525574, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.222000002861023, + "updateList": [], + "groups": [ + { + "sleep": 0.49300000071525574, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.222000002861023, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.222000002861023, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.7580000162124634, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.222000002861023, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.6010000109672546, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.222000002861023, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.813000202178955, + "assist": 2.3529999256134033 + } + ] + }, + { + "delta": 1.2569999694824219, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.813000202178955, + "assist": 2.3529999256134033 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-51", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 12, + "side": 1, + "sleep": 4.198, + "assist": 2.676, + "onUpdateList": false + }, + { + "subType": 14, + "side": 2, + "sleep": 0, + "assist": 3.938, + "onUpdateList": true + }, + { + "subType": 7, + "side": 2, + "sleep": 1.774, + "assist": 1.66, + "onUpdateList": true + }, + { + "subType": 1, + "side": 2, + "sleep": -0.19, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 1.983 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group3", + "floatType": 13, + "value": 1.9830000400543213 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 4.197999954223633, + "assist": 2.6760001182556152 + }, + { + "sleep": 0, + "assist": 3.937999963760376 + }, + { + "sleep": 1.7740000486373901, + "assist": 1.659999966621399 + }, + { + "sleep": -0.1899999976158142, + "assist": 1.9830000400543213 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-52", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 13, + "side": 1, + "sleep": 1.841, + "assist": 2.067, + "onUpdateList": true + }, + { + "subType": 22, + "side": 1, + "sleep": -1.178, + "assist": -1.975, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.777, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.149, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.813 + }, + { + "kind": "frame", + "delta": 0.007, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.636 + }, + { + "kind": "frame", + "delta": 0.817, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.991, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 2.812999963760376 + }, + { + "step": 4, + "entity": "group1", + "floatType": 119, + "value": 1.6360000371932983 + } + ], + "steps": [ + { + "delta": 0.7770000100135803, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.2869998812675476, + "assist": 0.5129996538162231 + }, + { + "sleep": -1.1779999732971191, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 0.14900000393390656, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.06599965691566467 + }, + { + "sleep": -1.1779999732971191, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 0.14900000393390656, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.06599965691566467 + }, + { + "sleep": 2.812999963760376, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 0.007000000216066837, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.05199965834617615 + }, + { + "sleep": 2.7990000247955322, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 0.007000000216066837, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.05199965834617615 + }, + { + "sleep": 1.6360000371932983, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 0.8169999718666077, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.975000023841858 + } + ] + }, + { + "delta": 1.9910000562667847, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.975000023841858 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-53", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 14, + "side": 1, + "sleep": 3.846, + "assist": -0.45, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.195, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.572, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.103, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -0.409 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.879, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.375 + }, + { + "kind": "frame", + "delta": 0.746, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": -0.4090000092983246 + }, + { + "step": 7, + "entity": "group0", + "floatType": 13, + "value": 2.375 + } + ], + "steps": [ + { + "delta": 0.19499999284744263, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.2609994411468506, + "assist": -0.44999998807907104 + } + ] + }, + { + "delta": 2.572000026702881, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.44999998807907104 + } + ] + }, + { + "delta": 0.10300000011920929, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.44999998807907104 + } + ] + }, + { + "delta": 0.10300000011920929, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.4090000092983246 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.4090000092983246 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.4090000092983246 + } + ] + }, + { + "delta": 2.878999948501587, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.4090000092983246 + } + ] + }, + { + "delta": 2.878999948501587, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.375 + } + ] + }, + { + "delta": 0.7459999918937683, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.628999948501587 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.628999948501587 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-54", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 25, + "side": 1, + "sleep": 0, + "assist": 1.51, + "onUpdateList": false + }, + { + "subType": 5, + "side": 1, + "sleep": 0.934, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.971, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.464, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.137 + }, + { + "kind": "frame", + "delta": 0.723, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.725 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 5.089 + }, + { + "kind": "frame", + "delta": 0.072, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 6.726 + }, + { + "kind": "frame", + "delta": 2.684, + "locked": true, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": 2.13700008392334 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 4.724999904632568 + }, + { + "step": 5, + "entity": "group1", + "floatType": 119, + "value": 5.089000225067139 + }, + { + "step": 7, + "entity": "group1", + "floatType": 13, + "value": 6.72599983215332 + } + ], + "steps": [ + { + "delta": 2.9709999561309814, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.5099999904632568 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4639999866485596, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.5099999904632568 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4639999866485596, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.13700008392334, + "assist": 1.5099999904632568 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7229999899864197, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.4140000343322754, + "assist": 0.7869999408721924 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7229999899864197, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.724999904632568, + "assist": 0.7869999408721924 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7229999899864197, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 4.724999904632568, + "assist": 0.7869999408721924 + }, + { + "sleep": 5.089000225067139, + "assist": 0 + } + ] + }, + { + "delta": 0.07199999690055847, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 4.6529998779296875, + "assist": 0.7149999141693115 + }, + { + "sleep": 5.017000198364258, + "assist": 0 + } + ] + }, + { + "delta": 0.07199999690055847, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 4.6529998779296875, + "assist": 0.7149999141693115 + }, + { + "sleep": 5.017000198364258, + "assist": 6.72599983215332 + } + ] + }, + { + "delta": 2.684000015258789, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.3579998016357422 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-55", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 1.316, + "onUpdateList": true + }, + { + "subType": 0, + "side": 1, + "sleep": 2.791, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.801, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.888, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.443, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.193, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.968 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.955, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 1.968000054359436 + }, + { + "step": 5, + "entity": "group2", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 1.8009999990463257, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.888000011444092, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.4429999887943268, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.19300000369548798, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.19300000369548798, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.968000054359436, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.19300000369548798, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.968000054359436, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.9280000925064087, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.9550000429153442, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7909998893737793, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-56", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 2, + "side": 1, + "sleep": 0, + "assist": -1.911, + "onUpdateList": true + }, + { + "subType": 3, + "side": 2, + "sleep": 3.103, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 24, + "side": 2, + "sleep": 0, + "assist": 0.594, + "onUpdateList": false + }, + { + "subType": 1, + "side": 1, + "sleep": 2.174, + "assist": 4.544, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 7.71 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 0.529 + }, + { + "kind": "frame", + "delta": 0.849, + "locked": true, + "count": 3 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 3.916 + }, + { + "kind": "frame", + "delta": 1.255, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.849, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 3.466 + }, + { + "kind": "frame", + "delta": 0.302, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 7.710000038146973 + }, + { + "step": 1, + "entity": "group3", + "floatType": 13, + "value": 0.5289999842643738 + }, + { + "step": 3, + "entity": "group3", + "floatType": 13, + "value": 3.9159998893737793 + }, + { + "step": 7, + "entity": "group3", + "floatType": 119, + "value": 3.4660000801086426 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 3.1029999256134033, + "assist": 7.710000038146973 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 2.1740000247955322, + "assist": 4.544000148773193 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group3", + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 3.1029999256134033, + "assist": 7.710000038146973 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 2.1740000247955322, + "assist": 0.5289999842643738 + } + ] + }, + { + "delta": 0.8489999771118164, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0.5559999942779541, + "assist": 5.163000106811523 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8489999771118164, + "updateList": [ + "group3", + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0.5559999942779541, + "assist": 5.163000106811523 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 0, + "assist": 3.9159998893737793 + } + ] + }, + { + "delta": 1.2549999952316284, + "updateList": [ + "group3", + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0, + "assist": 1.3980000019073486 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 0, + "assist": 0.1510002315044403 + } + ] + }, + { + "delta": 2.8489999771118164, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group3", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 3.4660000801086426, + "assist": 0 + } + ] + }, + { + "delta": 0.3019999861717224, + "updateList": [ + "group3", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.9110000133514404 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.593999981880188 + }, + { + "sleep": 3.4660000801086426, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-57", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 15, + "side": 2, + "sleep": 4.25, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 24, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 11, + "side": 1, + "sleep": 2.552, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.774, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.342, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.497, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.14, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.255, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.647, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.617, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 1.7740000486373901, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.25, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 0.34200000762939453, + "updateList": [], + "groups": [ + { + "sleep": 4.25, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 1.496999979019165, + "updateList": [], + "groups": [ + { + "sleep": 4.25, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 4.25, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 1.1399999856948853, + "updateList": [], + "groups": [ + { + "sleep": 4.25, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 1.1399999856948853, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 0.2549999952316284, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 2.6470000743865967, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + }, + { + "delta": 0.6169999837875366, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.552000045776367, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-58", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 20, + "side": 2, + "sleep": 0.997, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 18, + "side": 1, + "sleep": -1.534, + "assist": 2.272, + "onUpdateList": false + }, + { + "subType": 8, + "side": 1, + "sleep": 2.802, + "assist": 1.365, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.242, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 7.784 + }, + { + "kind": "frame", + "delta": 2.226, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 7.545 + }, + { + "kind": "frame", + "delta": 2.274, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 13, + "value": 7.783999919891357 + }, + { + "step": 3, + "entity": "group3", + "floatType": 13, + "value": 7.545000076293945 + } + ], + "steps": [ + { + "delta": 2.242000102996826, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -1.534000039100647, + "assist": 2.2720000743865967 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.242000102996826, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -1.534000039100647, + "assist": 7.783999919891357 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2260000705718994, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -1.534000039100647, + "assist": 3.332000255584717 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2260000705718994, + "updateList": [ + "group3", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -1.534000039100647, + "assist": 3.332000255584717 + }, + { + "sleep": 0, + "assist": 7.545000076293945 + } + ] + }, + { + "delta": 2.2739999294281006, + "updateList": [ + "group3", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -1.534000039100647, + "assist": 1.0580004453659058 + }, + { + "sleep": 0, + "assist": 5.270999431610107 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-59", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 9, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.122 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.315 + }, + { + "kind": "frame", + "delta": 1.65, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.836, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.494 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 7.721 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 3.121999979019165 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 7.315000057220459 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 1.49399995803833 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": 7.7210001945495605 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.121999979019165 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.315000057220459 + } + ] + }, + { + "delta": 1.649999976158142, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.664999961853027 + } + ] + }, + { + "delta": 1.8359999656677246, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.15700280666351318 + } + ] + }, + { + "delta": 1.8359999656677246, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.49399995803833, + "assist": 0.15700280666351318 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.7439999580383301, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 7.7210001945495605, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-60", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 3, + "side": 1, + "sleep": 3.451, + "assist": 3.476, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.219, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.734 + }, + { + "kind": "frame", + "delta": 1.993, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.425 + }, + { + "kind": "frame", + "delta": 2.255, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.553, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.476 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 0.734000027179718 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 7.425000190734863 + }, + { + "step": 6, + "entity": "group0", + "floatType": 13, + "value": 3.4760000705718994 + } + ], + "steps": [ + { + "delta": 2.2190001010894775, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.231999158859253, + "assist": 1.2569992542266846 + } + ] + }, + { + "delta": 2.2190001010894775, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.231999158859253, + "assist": 0.734000027179718 + } + ] + }, + { + "delta": 1.9930000305175781, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.9930000305175781, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.425000190734863 + } + ] + }, + { + "delta": 2.255000114440918, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.9149999618530273 + } + ] + }, + { + "delta": 2.552999973297119, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.552999973297119, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.4760000705718994 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-61", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 10, + "side": 1, + "sleep": 0, + "assist": 2.385, + "onUpdateList": true + }, + { + "subType": 10, + "side": 2, + "sleep": 4.766, + "assist": 0.228, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.865, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.553, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.483, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0.059 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": 0.05900000035762787 + } + ], + "steps": [ + { + "delta": 1.8650000095367432, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.765999794006348, + "assist": 0.2280000001192093 + } + ] + }, + { + "delta": 2.552999973297119, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.765999794006348, + "assist": 0.2280000001192093 + } + ] + }, + { + "delta": 2.4830000400543213, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.765999794006348, + "assist": 0.2280000001192093 + } + ] + }, + { + "delta": 2.4830000400543213, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.765999794006348, + "assist": 0.05900000035762787 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-62", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 4.65, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 6, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 15, + "side": 2, + "sleep": 4.413, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 4.74, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.5, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.606, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.284, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.026 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 119, + "value": 0 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 2.0260000228881836 + } + ], + "steps": [ + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.6500000953674316, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.413000106811523, + "assist": 0 + }, + { + "sleep": 4.739999771118164, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.6500000953674316, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.739999771118164, + "assist": 0 + } + ] + }, + { + "delta": 0.6060000061988831, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.6500000953674316, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.739999771118164, + "assist": 0 + } + ] + }, + { + "delta": 0.2840000092983246, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.082000255584717, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.739999771118164, + "assist": 0 + } + ] + }, + { + "delta": 0.2840000092983246, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.0260000228881836, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.739999771118164, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-63", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 5, + "side": 1, + "sleep": 0, + "assist": 0.501, + "onUpdateList": false + }, + { + "subType": 11, + "side": 1, + "sleep": 0.702, + "assist": 3.984, + "onUpdateList": true + }, + { + "subType": 17, + "side": 2, + "sleep": 0, + "assist": 4.445, + "onUpdateList": true + }, + { + "subType": 14, + "side": 2, + "sleep": 1.055, + "assist": 4.196, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.737, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 7.743 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 13, + "value": 7.743000030517578 + }, + { + "step": 2, + "entity": "group3", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 2.736999988555908, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.5009999871253967 + }, + { + "sleep": 0, + "assist": 1.246999740600586 + }, + { + "sleep": 0, + "assist": 1.7080004215240479 + }, + { + "sleep": 1.0549999475479126, + "assist": 4.196000099182129 + } + ] + }, + { + "delta": 2.736999988555908, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.5009999871253967 + }, + { + "sleep": 0, + "assist": 1.246999740600586 + }, + { + "sleep": 0, + "assist": 7.743000030517578 + }, + { + "sleep": 1.0549999475479126, + "assist": 4.196000099182129 + } + ] + }, + { + "delta": 2.736999988555908, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.5009999871253967 + }, + { + "sleep": 0, + "assist": 1.246999740600586 + }, + { + "sleep": 0, + "assist": 7.743000030517578 + }, + { + "sleep": 1.0549999475479126, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-64", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 0, + "side": 2, + "sleep": 4.969, + "assist": 1.144, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.008 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.029 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.819 + }, + { + "kind": "frame", + "delta": 1.08, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 5.007999897003174 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 6.0289998054504395 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 1.819000005722046 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.968999862670898, + "assist": 1.1440000534057617 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.769000053405762, + "assist": 0.9440000057220459 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.76900053024292, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.76900053024292, + "assist": 5.007999897003174 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.0289998054504395, + "assist": 5.007999897003174 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.819000005722046, + "assist": 5.007999897003174 + } + ] + }, + { + "delta": 1.0800000429153442, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.7679998874664307 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-65", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 0, + "assist": 2.412, + "onUpdateList": false + }, + { + "subType": 21, + "side": 2, + "sleep": 4.72, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 4.16 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 4.159999847412109 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.4119999408721924 + }, + { + "sleep": 4.159999847412109, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-66", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 23, + "side": 1, + "sleep": 4.979, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.501, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.791, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.101 + }, + { + "kind": "frame", + "delta": 2.455, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.846, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.113 + }, + { + "kind": "frame", + "delta": 1.859, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.421, + "locked": true, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 6.10099983215332 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": 5.11299991607666 + } + ], + "steps": [ + { + "delta": 0.5009999871253967, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.478000164031982, + "assist": 0 + } + ] + }, + { + "delta": 1.7910000085830688, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.896000325679779, + "assist": 0 + } + ] + }, + { + "delta": 1.7910000085830688, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.896000325679779, + "assist": 6.10099983215332 + } + ] + }, + { + "delta": 2.4549999237060547, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.8459999561309814, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.11299991607666, + "assist": 0 + } + ] + }, + { + "delta": 1.8589999675750732, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.394999623298645, + "assist": 0 + } + ] + }, + { + "delta": 0.42100000381469727, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.13199961185455322, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-67", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 1, + "side": 2, + "sleep": 1.807, + "assist": 3.906, + "onUpdateList": true + }, + { + "subType": 22, + "side": 2, + "sleep": 2.001, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 18, + "side": 2, + "sleep": 4.139, + "assist": 1.949, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": -2.892 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.376, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.284, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": -2.8919999599456787 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 1.8070000410079956, + "assist": 3.9059998989105225 + }, + { + "sleep": -2.8919999599456787, + "assist": 0 + }, + { + "sleep": 4.138999938964844, + "assist": 1.9490000009536743 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 1.0570000410079956, + "assist": 3.1559998989105225 + }, + { + "sleep": -2.8919999599456787, + "assist": 0 + }, + { + "sleep": 3.3889999389648438, + "assist": 1.1990000009536743 + } + ] + }, + { + "delta": 0.37599998712539673, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.0280001163482666 + }, + { + "sleep": -2.8919999599456787, + "assist": 0 + }, + { + "sleep": 2.261000156402588, + "assist": 0.0710001289844513 + } + ] + }, + { + "delta": 1.284000039100647, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -2.8919999599456787, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-68", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 8, + "side": 1, + "sleep": 2.179, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 4.855, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 4.333 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.794 + }, + { + "kind": "frame", + "delta": 0.863, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.411, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.308, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.057, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.902, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 4.333000183105469 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 6.794000148773193 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.178999900817871, + "assist": 0 + }, + { + "sleep": 4.333000183105469, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 6.794000148773193, + "assist": 0 + }, + { + "sleep": 4.333000183105469, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8629999756813049, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 5.931000232696533, + "assist": 0 + }, + { + "sleep": 3.4700002670288086, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8629999756813049, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 5.931000232696533, + "assist": 0 + }, + { + "sleep": 3.4700002670288086, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.4110000133514404, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.1089999675750732, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3079999685287476, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.05700000002980232, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.9019999504089355, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-69", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 2.323, + "assist": 0.047, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 0, + "assist": 1.483, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 0, + "assist": 0.275, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.802, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.802000045776367, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-70", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 4, + "side": 2, + "sleep": -1.233, + "assist": 2.819, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 4.626, + "assist": 3.33, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.232, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.638 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 119, + "value": 2.638000011444092 + } + ], + "steps": [ + { + "delta": 2.2320001125335693, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -1.2330000400543213, + "assist": 0 + }, + { + "sleep": 0.16200006008148193, + "assist": 0 + } + ] + }, + { + "delta": 2.2320001125335693, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -1.2330000400543213, + "assist": 0 + }, + { + "sleep": 2.638000011444092, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-71", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 17, + "side": 1, + "sleep": 4.857, + "assist": 4.521, + "onUpdateList": true + }, + { + "subType": 7, + "side": 2, + "sleep": 2.778, + "assist": 1.51, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -1.255 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.261 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 4.426 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": -1.2549999952316284 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 3, + "entity": "group1", + "floatType": 119, + "value": 2.260999917984009 + }, + { + "step": 4, + "entity": "group1", + "floatType": 13, + "value": 4.426000118255615 + }, + { + "step": 5, + "entity": "group1", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.85699987411499, + "assist": -1.2549999952316284 + }, + { + "sleep": 2.7780001163482666, + "assist": 1.5099999904632568 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.2549999952316284 + }, + { + "sleep": 2.7780001163482666, + "assist": 1.5099999904632568 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.7780001163482666, + "assist": 1.5099999904632568 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.260999917984009, + "assist": 1.5099999904632568 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.260999917984009, + "assist": 4.426000118255615 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.426000118255615 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-72", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 0, + "side": 2, + "sleep": 3.024, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.221, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 2.327, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.685, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.503, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.813, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.306, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.393, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.839, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.22100000083446503, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.0239999294281006, + "assist": 0 + } + ] + }, + { + "delta": 2.3269999027252197, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.684999942779541, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.684999942779541, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.503000020980835, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.812999963760376, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.305999994277954, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.3929998874664307, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.8389999866485596, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-73", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 25, + "side": 2, + "sleep": 1.466, + "assist": 4.275, + "onUpdateList": true + }, + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 4.102, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.359, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0.31 + }, + { + "kind": "frame", + "delta": 1.863, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.03, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0.155 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 6.81 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 0.3100000023841858 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": 0.1550000011920929 + }, + { + "step": 6, + "entity": "group1", + "floatType": 119, + "value": 6.809999942779541 + } + ], + "steps": [ + { + "delta": 0.35899999737739563, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.1069998741149902, + "assist": 3.9160003662109375 + }, + { + "sleep": 0, + "assist": 3.7430005073547363 + } + ] + }, + { + "delta": 0.35899999737739563, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.1069998741149902, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.7430005073547363 + } + ] + }, + { + "delta": 0.35899999737739563, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.1069998741149902, + "assist": 0 + }, + { + "sleep": 0.3100000023841858, + "assist": 3.7430005073547363 + } + ] + }, + { + "delta": 1.8630000352859497, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.0299999713897705, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.0299999713897705, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.1550000011920929 + } + ] + }, + { + "delta": 2.0299999713897705, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 6.809999942779541, + "assist": 0.1550000011920929 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-74", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 101, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 1.604, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 10, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 2.546, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 9, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.208, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.389, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 1.644 + }, + { + "kind": "frame", + "delta": 0.131, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 4.958 + }, + { + "kind": "frame", + "delta": 2.79, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": -1.739 + } + ] + }, + "expected": { + "result": "assert:(frame_rate >= 1) && (frame_rate <= 100)", + "transmissions": [], + "steps": [] + } + }, + { + "id": "timeline-20260923-75", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 15, + "side": 1, + "sleep": 0, + "assist": 1.979, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 19, + "side": 1, + "sleep": 2.189, + "assist": 0.086, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.1, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 3.721 + }, + { + "kind": "frame", + "delta": 2.56, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.23, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.662, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group2", + "floatType": 13, + "value": 0 + }, + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": 3.7209999561309814 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0.0860000029206276 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0.0860000029206276 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 3.7209999561309814 + }, + { + "sleep": 2.188999891281128, + "assist": 0 + } + ] + }, + { + "delta": 2.559999942779541, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0 + } + ] + }, + { + "delta": 1.2300000190734863, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0 + } + ] + }, + { + "delta": 1.6619999408721924, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.9789999723434448 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.188999891281128, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-76", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 2.408, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 21, + "side": 1, + "sleep": 4.99, + "assist": 2.36, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.248, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.73, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.126, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.803, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.517 + }, + { + "kind": "frame", + "delta": 1.201, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.74 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 3.211 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 3.5169999599456787 + }, + { + "step": 6, + "entity": "group0", + "floatType": 13, + "value": 2.740000009536743 + }, + { + "step": 7, + "entity": "group1", + "floatType": 13, + "value": 3.2109999656677246 + } + ], + "steps": [ + { + "delta": 0.24799999594688416, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.6640000343322754, + "assist": 0 + }, + { + "sleep": 4.245999336242676, + "assist": 1.6159999370574951 + } + ] + }, + { + "delta": 0.7300000190734863, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.055999279022217, + "assist": 0 + } + ] + }, + { + "delta": 1.1260000467300415, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.055999279022217, + "assist": 0 + } + ] + }, + { + "delta": 0.8029999732971191, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.055999279022217, + "assist": 0 + } + ] + }, + { + "delta": 0.8029999732971191, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.5169999599456787 + }, + { + "sleep": 2.055999279022217, + "assist": 0 + } + ] + }, + { + "delta": 1.2009999752044678, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.1150000095367432 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2009999752044678, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.740000009536743 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2009999752044678, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.740000009536743 + }, + { + "sleep": 0, + "assist": 3.2109999656677246 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-77", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 4, + "side": 2, + "sleep": 0, + "assist": 3.658, + "onUpdateList": false + }, + { + "subType": 16, + "side": 1, + "sleep": 3.309, + "assist": 0.049, + "onUpdateList": true + }, + { + "subType": 23, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 17, + "side": 1, + "sleep": 0, + "assist": 0.504, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0.661 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.222 + }, + { + "kind": "frame", + "delta": 1.429, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.653, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.577, + "locked": true, + "count": 0 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 2.526 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 0.6610000133514404 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 7.2220001220703125 + }, + { + "step": 5, + "entity": "group3", + "floatType": 13, + "value": 2.5260000228881836 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.6579999923706055 + }, + { + "sleep": 3.309000015258789, + "assist": 0.04899999871850014 + }, + { + "sleep": 0, + "assist": 0.6610000133514404 + }, + { + "sleep": 0, + "assist": 0.5040000081062317 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 7.2220001220703125 + }, + { + "sleep": 3.309000015258789, + "assist": 0.04899999871850014 + }, + { + "sleep": 0, + "assist": 0.6610000133514404 + }, + { + "sleep": 0, + "assist": 0.5040000081062317 + } + ] + }, + { + "delta": 1.4290000200271606, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.793000221252441 + }, + { + "sleep": 1.880000114440918, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.5040000081062317 + } + ] + }, + { + "delta": 1.652999997138977, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.4870011806488037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.5040000081062317 + } + ] + }, + { + "delta": 2.5769999027252197, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.4870011806488037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.5040000081062317 + } + ] + }, + { + "delta": 2.5769999027252197, + "updateList": [ + "group3", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.4870011806488037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.5260000228881836 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-78", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 9, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.653, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.319 + }, + { + "kind": "frame", + "delta": 1.78, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.597, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 7.495 + }, + { + "kind": "frame", + "delta": 2.194, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -0.345 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 6.318999767303467 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 7.494999885559082 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": -0.3449999988079071 + } + ], + "steps": [ + { + "delta": 2.6530001163482666, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.6530001163482666, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.318999767303467 + } + ] + }, + { + "delta": 1.7799999713897705, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.759000778198242 + } + ] + }, + { + "delta": 2.5969998836517334, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5969998836517334, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5969998836517334, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 7.494999885559082, + "assist": 0 + } + ] + }, + { + "delta": 2.194000005722046, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.301000118255615, + "assist": 0 + } + ] + }, + { + "delta": 2.194000005722046, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.3449999988079071, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-79", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 19, + "side": 2, + "sleep": 0, + "assist": 4.975, + "onUpdateList": true + }, + { + "subType": 1, + "side": 2, + "sleep": 0.549, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 24, + "side": 2, + "sleep": 0, + "assist": 1.499, + "onUpdateList": true + }, + { + "subType": 24, + "side": 1, + "sleep": 0.044, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.505, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.493, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.876, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 3, + "floatType": 13, + "value": 3.927 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.322, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.188, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 1.334 + }, + { + "kind": "frame", + "delta": 2.908, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.192, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group3", + "floatType": 13, + "value": 3.927000045776367 + }, + { + "step": 4, + "entity": "group2", + "floatType": 119, + "value": 0 + }, + { + "step": 7, + "entity": "group2", + "floatType": 119, + "value": 1.3339999914169312 + } + ], + "steps": [ + { + "delta": 2.505000114440918, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.49300000071525574, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.8760000467300415, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.8760000467300415, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.927000045776367 + } + ] + }, + { + "delta": 1.8760000467300415, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.927000045776367 + } + ] + }, + { + "delta": 1.3220000267028809, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.6050002574920654 + } + ] + }, + { + "delta": 2.187999963760376, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.187999963760376, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.3339999914169312, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.9079999923706055, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.191999912261963, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-80", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 20, + "side": 1, + "sleep": -0.835, + "assist": 4.631, + "onUpdateList": true + }, + { + "subType": 8, + "side": 2, + "sleep": 1.889, + "assist": 1.288, + "onUpdateList": false + }, + { + "subType": 16, + "side": 1, + "sleep": 0.853, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.966, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0.701 + }, + { + "kind": "frame", + "delta": 1.566, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.612, + "locked": true, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 119, + "value": 0.7009999752044678 + } + ], + "steps": [ + { + "delta": 1.965999960899353, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.8349999785423279, + "assist": 2.6650002002716064 + }, + { + "sleep": 1.8890000581741333, + "assist": 1.2879999876022339 + }, + { + "sleep": 0.8529999852180481, + "assist": 0 + } + ] + }, + { + "delta": 1.965999960899353, + "updateList": [ + "group2", + "group0" + ], + "groups": [ + { + "sleep": -0.8349999785423279, + "assist": 2.6650002002716064 + }, + { + "sleep": 1.8890000581741333, + "assist": 1.2879999876022339 + }, + { + "sleep": 0.7009999752044678, + "assist": 0 + } + ] + }, + { + "delta": 1.565999984741211, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.8349999785423279, + "assist": 0 + }, + { + "sleep": 1.8890000581741333, + "assist": 1.2879999876022339 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.6119999885559082, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -0.8349999785423279, + "assist": 0 + }, + { + "sleep": 1.8890000581741333, + "assist": 1.2879999876022339 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-81", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 0.274, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 12, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.164, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 3.206 + }, + { + "kind": "frame", + "delta": 1.522, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 4.457 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.397 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -0.91 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 13, + "value": 3.2060000896453857 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": 4.456999778747559 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": 6.396999835968018 + }, + { + "step": 8, + "entity": "group0", + "floatType": 119, + "value": -0.9100000262260437 + } + ], + "steps": [ + { + "delta": 2.1640000343322754, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.1640000343322754, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.2060000896453857 + } + ] + }, + { + "delta": 1.5219999551773071, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.456999778747559 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 6.396999835968018, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.456999778747559 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 6.35699987411499, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.416999816894531 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -0.9100000262260437, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.416999816894531 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-82", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 14, + "side": 2, + "sleep": 0.254, + "assist": 2.013, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.931, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.095, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.758, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.72 + }, + { + "kind": "frame", + "delta": 0.436, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": 6.71999979019165 + } + ], + "steps": [ + { + "delta": 0.9309999942779541, + "updateList": [], + "groups": [ + { + "sleep": 0.2540000081062317, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [], + "groups": [ + { + "sleep": 0.2540000081062317, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 2.0950000286102295, + "updateList": [], + "groups": [ + { + "sleep": 0.2540000081062317, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0.2540000081062317, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 0.25, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 2.757999897003174, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 2.757999897003174, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.71999979019165, + "assist": 2.013000011444092 + } + ] + }, + { + "delta": 0.4359999895095825, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.283998966217041, + "assist": 1.5770001411437988 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-83", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 25, + "side": 1, + "sleep": 0, + "assist": 4.588, + "onUpdateList": false + }, + { + "subType": 7, + "side": 1, + "sleep": 3.624, + "assist": 4.439, + "onUpdateList": true + }, + { + "subType": 14, + "side": 2, + "sleep": 1.132, + "assist": -1.228, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.462, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.515, + "locked": true, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.5, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.5879998207092285 + }, + { + "sleep": 2.62399959564209, + "assist": 3.439000129699707 + }, + { + "sleep": 1.1319999694824219, + "assist": -1.2280000448226929 + } + ] + }, + { + "delta": 0.4620000123977661, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.5879998207092285 + }, + { + "sleep": 1.2379992008209229, + "assist": 2.05299973487854 + }, + { + "sleep": 1.1319999694824219, + "assist": -1.2280000448226929 + } + ] + }, + { + "delta": 2.515000104904175, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.5879998207092285 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.1319999694824219, + "assist": -1.2280000448226929 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-84", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 18, + "side": 1, + "sleep": 2.75, + "assist": 3.259, + "onUpdateList": true + }, + { + "subType": 5, + "side": 2, + "sleep": 3.28, + "assist": 3.981, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.071, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.071000099182129, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2799999713897705, + "assist": 3.9809999465942383 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-85", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 100, + "groups": [ + { + "subType": 24, + "side": 1, + "sleep": 0, + "assist": 3.456, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 2.362, + "assist": 1.967, + "onUpdateList": true + }, + { + "subType": 20, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.885, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.357, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.018, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.3, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.553, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.739, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 8, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.8849999904632568, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 1.4769999980926514, + "assist": 1.0820000171661377 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3569999933242798, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.017999999225139618, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.30000001192092896, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.552999973297119, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.739000082015991, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.4560000896453857 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.739000082015991, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-86", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 23, + "side": 1, + "sleep": 4.477, + "assist": 2.998, + "onUpdateList": true + }, + { + "subType": 22, + "side": 2, + "sleep": -0.881, + "assist": 1.346, + "onUpdateList": true + }, + { + "subType": 10, + "side": 1, + "sleep": 4.869, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 16, + "side": 2, + "sleep": 4.899, + "assist": 0.205, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.173 + }, + { + "kind": "frame", + "delta": 0.926, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.974, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 6.172999858856201 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 6.172999858856201, + "assist": 2.997999906539917 + }, + { + "sleep": -0.8809999823570251, + "assist": 1.3459999561309814 + }, + { + "sleep": 4.86899995803833, + "assist": 0 + }, + { + "sleep": 4.89900016784668, + "assist": 0.20499999821186066 + } + ] + }, + { + "delta": 0.9259999990463257, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 5.246999740600586, + "assist": 2.072000026702881 + }, + { + "sleep": -0.8809999823570251, + "assist": 0.41999995708465576 + }, + { + "sleep": 4.86899995803833, + "assist": 0 + }, + { + "sleep": 4.89900016784668, + "assist": 0.20499999821186066 + } + ] + }, + { + "delta": 1.9739999771118164, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -0.8809999823570251, + "assist": 0 + }, + { + "sleep": 4.86899995803833, + "assist": 0 + }, + { + "sleep": 4.89900016784668, + "assist": 0.20499999821186066 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-87", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 25, + "side": 1, + "sleep": 0.007, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 7, + "side": 2, + "sleep": 4.219, + "assist": 3.422, + "onUpdateList": false + }, + { + "subType": 15, + "side": 1, + "sleep": 0, + "assist": 0.986, + "onUpdateList": false + }, + { + "subType": 16, + "side": 1, + "sleep": 2.31, + "assist": 4.385, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.671, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.97, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.813, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.264, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.6710000038146973, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0.007000000216066837, + "assist": 0 + }, + { + "sleep": 4.218999862670898, + "assist": 3.421999931335449 + }, + { + "sleep": 0, + "assist": 0.9860000014305115 + }, + { + "sleep": 0.9679999351501465, + "assist": 3.0430004596710205 + } + ] + }, + { + "delta": 0.9700000286102295, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0.007000000216066837, + "assist": 0 + }, + { + "sleep": 4.218999862670898, + "assist": 3.421999931335449 + }, + { + "sleep": 0, + "assist": 0.9860000014305115 + }, + { + "sleep": 0, + "assist": 2.073000431060791 + } + ] + }, + { + "delta": 0.8130000233650208, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0.007000000216066837, + "assist": 0 + }, + { + "sleep": 4.218999862670898, + "assist": 3.421999931335449 + }, + { + "sleep": 0, + "assist": 0.9860000014305115 + }, + { + "sleep": 0, + "assist": 1.260000467300415 + } + ] + }, + { + "delta": 1.2640000581741333, + "updateList": [], + "groups": [ + { + "sleep": 0.007000000216066837, + "assist": 0 + }, + { + "sleep": 4.218999862670898, + "assist": 3.421999931335449 + }, + { + "sleep": 0, + "assist": 0.9860000014305115 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-88", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 23, + "side": 2, + "sleep": 4.581, + "assist": 3.622, + "onUpdateList": false + }, + { + "subType": 0, + "side": 1, + "sleep": 4.076, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.03999999910593033, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.580999851226807, + "assist": 3.621999979019165 + }, + { + "sleep": 3.996000289916992, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-89", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 16, + "side": 2, + "sleep": 0, + "assist": 2.164, + "onUpdateList": false + }, + { + "subType": 7, + "side": 2, + "sleep": 1.053, + "assist": 2.976, + "onUpdateList": false + }, + { + "subType": 12, + "side": 1, + "sleep": 0, + "assist": 0.057, + "onUpdateList": false + }, + { + "subType": 22, + "side": 1, + "sleep": -1.84, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.785, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.037, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.98, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.499, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.982, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.7850000262260437, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + }, + { + "delta": 0.03700000047683716, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + }, + { + "delta": 0.9800000190734863, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + }, + { + "delta": 0.49900001287460327, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + }, + { + "delta": 2.9820001125335693, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.1640000343322754 + }, + { + "sleep": 1.0529999732971191, + "assist": 2.9760000705718994 + }, + { + "sleep": 0, + "assist": 0.05700000002980232 + }, + { + "sleep": -1.840000033378601, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-90", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 23, + "side": 1, + "sleep": -0.473, + "assist": 4.793, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.785 + }, + { + "kind": "frame", + "delta": 0.188, + "locked": true, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.9 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.597 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.256 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -2.339 + }, + { + "kind": "frame", + "delta": 1.073, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.591, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -1.384 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 0.7850000262260437 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 0.8999999761581421 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 6.5970001220703125 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 0.25600001215934753 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": -2.3389999866485596 + }, + { + "step": 8, + "entity": "group0", + "floatType": 119, + "value": -1.3839999437332153 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.7850000262260437, + "assist": 4.793000221252441 + } + ] + }, + { + "delta": 0.18799999356269836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.22100001573562622, + "assist": 4.228999614715576 + } + ] + }, + { + "delta": 0.18799999356269836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.22100001573562622, + "assist": 0.8999999761581421 + } + ] + }, + { + "delta": 0.18799999356269836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.22100001573562622, + "assist": 6.5970001220703125 + } + ] + }, + { + "delta": 0.18799999356269836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.25600001215934753, + "assist": 6.5970001220703125 + } + ] + }, + { + "delta": 0.18799999356269836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.25600001215934753, + "assist": -2.3389999866485596 + } + ] + }, + { + "delta": 1.0729999542236328, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -2.3389999866485596 + } + ] + }, + { + "delta": 0.5910000205039978, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -2.3389999866485596 + } + ] + }, + { + "delta": 0.5910000205039978, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.3839999437332153, + "assist": -2.3389999866485596 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-91", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 7, + "side": 2, + "sleep": 4.425, + "assist": 3.608, + "onUpdateList": false + }, + { + "subType": 23, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 7, + "side": 2, + "sleep": 3.291, + "assist": 2.159, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 1.488 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.993, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 1.4880000352859497 + }, + { + "step": 1, + "entity": "group3", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.425000190734863, + "assist": 1.4880000352859497 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2909998893737793, + "assist": 2.1589999198913574 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.425000190734863, + "assist": 1.4880000352859497 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.1589999198913574 + } + ] + }, + { + "delta": 0.9929999709129333, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.446000099182129, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-92", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 14, + "side": 2, + "sleep": 0, + "assist": 2.093, + "onUpdateList": true + }, + { + "subType": 8, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.192, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.581 + }, + { + "kind": "frame", + "delta": 1.648, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.386 + }, + { + "kind": "frame", + "delta": 2.806, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.395, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 6.282 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group1", + "floatType": 119, + "value": 0 + }, + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 1.5809999704360962 + }, + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 6.386000156402588 + }, + { + "step": 7, + "entity": "group1", + "floatType": 119, + "value": 6.2820000648498535 + } + ], + "steps": [ + { + "delta": 1.1920000314712524, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.1920000314712524, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.1920000314712524, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5809999704360962, + "assist": 0 + } + ] + }, + { + "delta": 1.6480000019073486, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.6480000019073486, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.386000156402588 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.805999994277954, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.386000156402588 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.39500001072883606, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.201000213623047 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.39500001072883606, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.201000213623047 + }, + { + "sleep": 6.2820000648498535, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-93", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 2, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 6, + "side": 2, + "sleep": 2.196, + "assist": 2.555, + "onUpdateList": false + }, + { + "subType": 19, + "side": 1, + "sleep": 0, + "assist": 1.872, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 2.678 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.623 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.413 + }, + { + "kind": "frame", + "delta": 0.954, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.301, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 2.677999973297119 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 2.622999906539917 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 2.4130001068115234 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.196000099182129, + "assist": 2.555000066757202 + }, + { + "sleep": 0, + "assist": 2.677999973297119 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 2.622999906539917, + "assist": 0 + }, + { + "sleep": 2.196000099182129, + "assist": 2.555000066757202 + }, + { + "sleep": 0, + "assist": 2.677999973297119 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 2.622999906539917, + "assist": 2.4130001068115234 + }, + { + "sleep": 2.196000099182129, + "assist": 2.555000066757202 + }, + { + "sleep": 0, + "assist": 2.677999973297119 + } + ] + }, + { + "delta": 0.9539999961853027, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.196000099182129, + "assist": 2.555000066757202 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3009999990463257, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.196000099182129, + "assist": 2.555000066757202 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-94", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 25, + "side": 2, + "sleep": 2.131, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.382, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.495, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.82 + }, + { + "kind": "frame", + "delta": 1.348, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group1", + "floatType": 119, + "value": 1.8200000524520874 + } + ], + "steps": [ + { + "delta": 2.381999969482422, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.13100004196167, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.13100004196167, + "assist": 0 + } + ] + }, + { + "delta": 1.4950000047683716, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.13100004196167, + "assist": 0 + } + ] + }, + { + "delta": 1.4950000047683716, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.8200000524520874, + "assist": 0 + } + ] + }, + { + "delta": 1.3480000495910645, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.8200000524520874, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-95", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 0, + "side": 2, + "sleep": 1.757, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 4, + "side": 2, + "sleep": 0.966, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 1, + "side": 1, + "sleep": 3.225, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 7, + "side": 1, + "sleep": 4.988, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.381, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.707, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.338, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 2.455, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.012, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 1.066 + }, + { + "kind": "frame", + "delta": 0.798, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group3", + "floatType": 119, + "value": 0 + }, + { + "step": 6, + "entity": "group3", + "floatType": 119, + "value": 1.065999984741211 + } + ], + "steps": [ + { + "delta": 1.38100004196167, + "updateList": [ + "group2", + "group3" + ], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.4629998207092285, + "assist": 0 + }, + { + "sleep": 2.2259998321533203, + "assist": 0 + } + ] + }, + { + "delta": 2.7070000171661377, + "updateList": [], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.7070000171661377, + "updateList": [], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3380000591278076, + "updateList": [], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.4549999237060547, + "updateList": [], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.01200008392334, + "updateList": [], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.01200008392334, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.065999984741211, + "assist": 0 + } + ] + }, + { + "delta": 0.7979999780654907, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 1.7569999694824219, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.065999984741211, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-96", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 5, + "side": 1, + "sleep": 0, + "assist": 4.255, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.031 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.124 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.76, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.936, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.736 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 2.0309998989105225 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 3.124000072479248 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 2.7360000610351562 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.0309998989105225, + "assist": 4.255000114440918 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.0309998989105225, + "assist": 3.124000072479248 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.9309998750686646, + "assist": 3.0240001678466797 + } + ] + }, + { + "delta": 1.7599999904632568, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.17099978029727936, + "assist": 1.264000654220581 + } + ] + }, + { + "delta": 2.936000108718872, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.17099978029727936, + "assist": 1.264000654220581 + } + ] + }, + { + "delta": 2.936000108718872, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.7360000610351562, + "assist": 1.264000654220581 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-97", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 20, + "side": 1, + "sleep": 0, + "assist": 2.492, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.815, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.8149999976158142, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.6770001649856567 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.5570002794265747 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-98", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 7, + "side": 1, + "sleep": 0.089, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 24, + "side": 2, + "sleep": 1.227, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.238 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 2.23799991607666 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 0.08900000154972076, + "assist": 0 + }, + { + "sleep": 2.23799991607666, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-99", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 16, + "side": 2, + "sleep": 2.27, + "assist": -0.864, + "onUpdateList": false + }, + { + "subType": 23, + "side": 1, + "sleep": 1.335, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 8, + "side": 1, + "sleep": 0, + "assist": 2.091, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 4.055 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 2.964, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.955, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.874, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.095, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.146, + "locked": true, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 4.054999828338623 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 1.3350000381469727, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.054999828338623 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 1.3350000381469727, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.054999828338623 + } + ] + }, + { + "delta": 2.9639999866485596, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.0910000801086426 + } + ] + }, + { + "delta": 0.9549999833106995, + "updateList": [], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.8739999532699585, + "updateList": [], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.0950000286102295, + "updateList": [], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.1459999084472656, + "updateList": [], + "groups": [ + { + "sleep": 2.2699999809265137, + "assist": -0.8640000224113464 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-100", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 14, + "side": 1, + "sleep": 0, + "assist": 3.183, + "onUpdateList": false + }, + { + "subType": 11, + "side": 2, + "sleep": 3.357, + "assist": 2.572, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 0.169, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 21, + "side": 1, + "sleep": 0, + "assist": 0.466, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.782, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.857, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.195, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.246, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.907, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.816, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.246, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.344, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.781999945640564, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8569999933242798, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.194999933242798, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.2460000067949295, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.9070000648498535, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8159999847412109, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.246000051498413, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.3440001010894775, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.183000087738037 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.16899999976158142, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-101", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 7, + "side": 1, + "sleep": 0, + "assist": 1.249, + "onUpdateList": false + }, + { + "subType": 3, + "side": 1, + "sleep": 2.262, + "assist": -1.396, + "onUpdateList": false + }, + { + "subType": 0, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.298, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": -2.963 + }, + { + "kind": "frame", + "delta": 0.731, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 6.528 + }, + { + "kind": "frame", + "delta": 1.462, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.532, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": -1.874 + }, + { + "kind": "frame", + "delta": 2.613, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 119, + "value": -2.9630000591278076 + }, + { + "step": 3, + "entity": "group1", + "floatType": 13, + "value": 6.5279998779296875 + }, + { + "step": 6, + "entity": "group2", + "floatType": 119, + "value": 0 + }, + { + "step": 7, + "entity": "group1", + "floatType": 13, + "value": -1.8739999532699585 + }, + { + "step": 9, + "entity": "group2", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 2.2980000972747803, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 2.26200008392334, + "assist": -1.3960000276565552 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.2980000972747803, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 2.26200008392334, + "assist": -1.3960000276565552 + }, + { + "sleep": -2.9630000591278076, + "assist": 0 + } + ] + }, + { + "delta": 0.7310000061988831, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 2.26200008392334, + "assist": -1.3960000276565552 + }, + { + "sleep": -2.9630000591278076, + "assist": 0 + } + ] + }, + { + "delta": 0.7310000061988831, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 2.26200008392334, + "assist": 6.5279998779296875 + }, + { + "sleep": -2.9630000591278076, + "assist": 0 + } + ] + }, + { + "delta": 1.4620000123977661, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": 3.603996992111206 + }, + { + "sleep": -2.9630000591278076, + "assist": 0 + } + ] + }, + { + "delta": 2.5320000648498535, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": 1.0719975233078003 + }, + { + "sleep": -2.9630000591278076, + "assist": 0 + } + ] + }, + { + "delta": 2.5320000648498535, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": 1.0719975233078003 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5320000648498535, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": -1.8739999532699585 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.61299991607666, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": -1.8739999532699585 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.61299991607666, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.2489999532699585 + }, + { + "sleep": 0, + "assist": -1.8739999532699585 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-102", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 19, + "side": 2, + "sleep": 1.28, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.363, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.709, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.155, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 4.91 + }, + { + "kind": "frame", + "delta": 0.651, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.45, + "locked": true, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.012 + }, + { + "kind": "frame", + "delta": 1.046, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 4.909999847412109 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 8, + "entity": "group0", + "floatType": 13, + "value": 6.01200008392334 + } + ], + "steps": [ + { + "delta": 0.3630000054836273, + "updateList": [], + "groups": [ + { + "sleep": 1.2799999713897705, + "assist": 0 + } + ] + }, + { + "delta": 1.7089999914169312, + "updateList": [], + "groups": [ + { + "sleep": 1.2799999713897705, + "assist": 0 + } + ] + }, + { + "delta": 2.1549999713897705, + "updateList": [], + "groups": [ + { + "sleep": 1.2799999713897705, + "assist": 0 + } + ] + }, + { + "delta": 2.1549999713897705, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.909999847412109, + "assist": 0 + } + ] + }, + { + "delta": 0.6510000228881836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.258999824523926, + "assist": 0 + } + ] + }, + { + "delta": 0.6510000228881836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4500000476837158, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4500000476837158, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.01200008392334 + } + ] + }, + { + "delta": 1.0460000038146973, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.966000556945801 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-103", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 8, + "side": 1, + "sleep": 3.908, + "assist": 1.544, + "onUpdateList": false + }, + { + "subType": 20, + "side": 2, + "sleep": 0, + "assist": 1.783, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 1.047, + "assist": -0.647, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 2.61 + }, + { + "kind": "frame", + "delta": 0.732, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 2.656 + }, + { + "kind": "frame", + "delta": 1.433, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 0.22, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 2.334 + }, + { + "kind": "frame", + "delta": 0.471, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.263, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 2.609999895095825 + }, + { + "step": 2, + "entity": "group1", + "floatType": 13, + "value": 2.6559998989105225 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 2.3340001106262207 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.9079999923706055, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 1.7829999923706055 + }, + { + "sleep": 1.0470000505447388, + "assist": 2.609999895095825 + } + ] + }, + { + "delta": 0.7319999933242798, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.9079999923706055, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 1.0510001182556152 + }, + { + "sleep": 0.31500011682510376, + "assist": 1.878000020980835 + } + ] + }, + { + "delta": 0.7319999933242798, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.9079999923706055, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 2.6559998989105225 + }, + { + "sleep": 0.31500011682510376, + "assist": 1.878000020980835 + } + ] + }, + { + "delta": 1.4329999685287476, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.9079999923706055, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 2.6559998989105225 + }, + { + "sleep": 0.31500011682510376, + "assist": 1.878000020980835 + } + ] + }, + { + "delta": 0.2199999988079071, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 3.9079999923706055, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 2.2160003185272217 + }, + { + "sleep": 0, + "assist": 1.437999963760376 + } + ] + }, + { + "delta": 0.2199999988079071, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 2.3340001106262207, + "assist": 1.5440000295639038 + }, + { + "sleep": 0, + "assist": 2.2160003185272217 + }, + { + "sleep": 0, + "assist": 1.437999963760376 + } + ] + }, + { + "delta": 0.47099998593330383, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 1.3920001983642578, + "assist": 0.6020001769065857 + }, + { + "sleep": 0, + "assist": 1.2740005254745483 + }, + { + "sleep": 0, + "assist": 0.4960000514984131 + } + ] + }, + { + "delta": 0.2630000114440918, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.6030001640319824, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.48500049114227295 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-104", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 14, + "side": 2, + "sleep": 4.562, + "assist": 4.93, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.189 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.085, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": true, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 6.189000129699707 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.361999988555908, + "assist": 4.730000019073486 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.730000019073486 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.690000057220459 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 4.440000057220459 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 6.189000129699707 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.689000129699707 + } + ] + }, + { + "delta": 0.08500000089406967, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.519000053405762 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.219000339508057 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-105", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 16, + "side": 1, + "sleep": 4.136, + "assist": 4.51, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.267 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.49, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.675, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.534, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.834, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.319, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.052, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 2.2669999599456787 + }, + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.136000156402588, + "assist": 2.2669999599456787 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.2669999599456787 + } + ] + }, + { + "delta": 2.490000009536743, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.674999952316284, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.534000039100647, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.8339999914169312, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.319000005722046, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.0520000457763672, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-106", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 0, + "groups": [ + { + "subType": 20, + "side": 1, + "sleep": 1.089, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 18, + "side": 2, + "sleep": 4.582, + "assist": 2.854, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.038, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.663, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.156, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.638 + }, + { + "kind": "frame", + "delta": 2.209, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.943, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.988, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.485, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.036 + } + ] + }, + "expected": { + "result": "assert:(frame_rate >= 1) && (frame_rate <= 100)", + "transmissions": [], + "steps": [] + } + }, + { + "id": "timeline-20260923-107", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 21, + "side": 1, + "sleep": 0.568, + "assist": 1.892, + "onUpdateList": false + }, + { + "subType": 1, + "side": 2, + "sleep": 2.718, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.97 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -0.645 + }, + { + "kind": "frame", + "delta": 0.55, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.945, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.526, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.278, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.122, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 5.96999979019165 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": -0.6449999809265137 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.5680000185966492, + "assist": 5.96999979019165 + }, + { + "sleep": 2.7179999351501465, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.5680000185966492, + "assist": -0.6449999809265137 + }, + { + "sleep": 2.7179999351501465, + "assist": 0 + } + ] + }, + { + "delta": 0.550000011920929, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.018000006675720215, + "assist": -0.6449999809265137 + }, + { + "sleep": 2.1679999828338623, + "assist": 0 + } + ] + }, + { + "delta": 0.9449999928474426, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6449999809265137 + }, + { + "sleep": 1.2230000495910645, + "assist": 0 + } + ] + }, + { + "delta": 0.5260000228881836, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6449999809265137 + }, + { + "sleep": 0.17100000381469727, + "assist": 0 + } + ] + }, + { + "delta": 0.27799999713897705, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6449999809265137 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.121999979019165, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -0.6449999809265137 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-108", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 8, + "side": 2, + "sleep": 2.979, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 20, + "side": 1, + "sleep": 3.939, + "assist": 0.001, + "onUpdateList": true + }, + { + "subType": 0, + "side": 1, + "sleep": 2.789, + "assist": 3.86, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.236, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.2359999418258667, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 2.9790000915527344, + "assist": 0 + }, + { + "sleep": 2.7029998302459717, + "assist": 0 + }, + { + "sleep": 2.7890000343322754, + "assist": 3.859999895095825 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-109", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 7, + "side": 1, + "sleep": 2.814, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 24, + "side": 1, + "sleep": 0, + "assist": 2.908, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 6.336 + }, + { + "kind": "frame", + "delta": 0.804, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.397, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.34 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 6.426 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.832, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 119, + "value": 6.335999965667725 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 5.340000152587891 + }, + { + "step": 4, + "entity": "group1", + "floatType": 119, + "value": 6.426000118255615 + }, + { + "step": 5, + "entity": "group2", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group2", + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.813999891281128, + "assist": 0 + }, + { + "sleep": 6.335999965667725, + "assist": 2.9079999923706055 + } + ] + }, + { + "delta": 0.8040000200271606, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.813999891281128, + "assist": 0 + }, + { + "sleep": 4.728000164031982, + "assist": 1.3000001907348633 + } + ] + }, + { + "delta": 0.3970000147819519, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.813999891281128, + "assist": 0 + }, + { + "sleep": 3.9340004920959473, + "assist": 0.506000280380249 + } + ] + }, + { + "delta": 0.3970000147819519, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 5.340000152587891, + "assist": 0 + }, + { + "sleep": 2.813999891281128, + "assist": 0 + }, + { + "sleep": 3.9340004920959473, + "assist": 0.506000280380249 + } + ] + }, + { + "delta": 0.3970000147819519, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 5.340000152587891, + "assist": 0 + }, + { + "sleep": 6.426000118255615, + "assist": 0 + }, + { + "sleep": 3.9340004920959473, + "assist": 0.506000280380249 + } + ] + }, + { + "delta": 0.3970000147819519, + "updateList": [ + "group1", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 5.340000152587891, + "assist": 0 + }, + { + "sleep": 6.426000118255615, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.506000280380249 + } + ] + }, + { + "delta": 2.8320000171661377, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.7619985342025757, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-110", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 0.956, + "onUpdateList": true + }, + { + "subType": 15, + "side": 1, + "sleep": 4.612, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 22, + "side": 2, + "sleep": 1.217, + "assist": 4.152, + "onUpdateList": true + }, + { + "subType": 3, + "side": 2, + "sleep": 1.541, + "assist": 2.359, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.284, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.145, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0.476 + }, + { + "kind": "frame", + "delta": 2.719, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.106, + "locked": true, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 0.47600001096725464 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.9559999704360962 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 1.2170000076293945, + "assist": 4.1519999504089355 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 1.284000039100647, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 2.8679990768432617 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 1.1449999809265137, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.5779983997344971 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 1.1449999809265137, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0.47600001096725464, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.5779983997344971 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 2.7190001010894775, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 2.7190001010894775, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + }, + { + "delta": 1.1059999465942383, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.611999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5410000085830688, + "assist": 2.3589999675750732 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-111", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 22, + "side": 1, + "sleep": 0.444, + "assist": 4.562, + "onUpdateList": true + }, + { + "subType": 21, + "side": 2, + "sleep": 3.279, + "assist": 4.157, + "onUpdateList": false + }, + { + "subType": 15, + "side": 2, + "sleep": 0.373, + "assist": 4.801, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.678, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.677999973297119, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.8840011358261108 + }, + { + "sleep": 3.2790000438690186, + "assist": 4.1570000648498535 + }, + { + "sleep": 0.37299999594688416, + "assist": 4.801000118255615 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-112", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 17, + "side": 1, + "sleep": 0, + "assist": -1.032, + "onUpdateList": false + }, + { + "subType": 9, + "side": 2, + "sleep": 3.632, + "assist": 2.503, + "onUpdateList": false + }, + { + "subType": 18, + "side": 1, + "sleep": 1.223, + "assist": 1.585, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 5.248 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.46 + }, + { + "kind": "frame", + "delta": 0.044, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.356, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.768, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.962, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group2", + "floatType": 13, + "value": 5.248000144958496 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 3.4600000381469727 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.031999945640564 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 1.2230000495910645, + "assist": 5.248000144958496 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.4600000381469727 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 1.2230000495910645, + "assist": 5.248000144958496 + } + ] + }, + { + "delta": 0.04399999976158142, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.4160001277923584 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 1.1790000200271606, + "assist": 5.203999996185303 + } + ] + }, + { + "delta": 1.3559999465942383, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.0599992275238037 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 0, + "assist": 3.847999095916748 + } + ] + }, + { + "delta": 2.7679998874664307, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 0, + "assist": 1.0799983739852905 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 0, + "assist": 0.8299983739852905 + } + ] + }, + { + "delta": 2.9619998931884766, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.631999969482422, + "assist": 2.503000020980835 + }, + { + "sleep": 0, + "assist": 0.8299983739852905 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-113", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 8, + "side": 2, + "sleep": 0.556, + "assist": 3.601, + "onUpdateList": false + }, + { + "subType": 25, + "side": 1, + "sleep": 2.544, + "assist": 0.746, + "onUpdateList": true + }, + { + "subType": 2, + "side": 2, + "sleep": 1, + "assist": 2.388, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.082, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.392, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 4.193 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.742 + }, + { + "kind": "frame", + "delta": 0.439, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.027 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group2", + "floatType": 119, + "value": 4.192999839782715 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 3.742000102996826 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": 0.027000000700354576 + } + ], + "steps": [ + { + "delta": 0.0820000022649765, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0.5559999942779541, + "assist": 3.6010000705718994 + }, + { + "sleep": 2.4619998931884766, + "assist": 0.6639999747276306 + }, + { + "sleep": 1, + "assist": 2.388000011444092 + } + ] + }, + { + "delta": 0.3919999897480011, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0.5559999942779541, + "assist": 3.6010000705718994 + }, + { + "sleep": 1.2860000133514404, + "assist": 0 + }, + { + "sleep": 1, + "assist": 2.388000011444092 + } + ] + }, + { + "delta": 0.3919999897480011, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0.5559999942779541, + "assist": 3.6010000705718994 + }, + { + "sleep": 1.2860000133514404, + "assist": 0 + }, + { + "sleep": 4.192999839782715, + "assist": 2.388000011444092 + } + ] + }, + { + "delta": 0.3919999897480011, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 3.742000102996826, + "assist": 3.6010000705718994 + }, + { + "sleep": 1.2860000133514404, + "assist": 0 + }, + { + "sleep": 4.192999839782715, + "assist": 2.388000011444092 + } + ] + }, + { + "delta": 0.4390000104904175, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 3.302999973297119, + "assist": 3.1620001792907715 + }, + { + "sleep": 0.847000002861023, + "assist": 0 + }, + { + "sleep": 3.753999710083008, + "assist": 1.9490000009536743 + } + ] + }, + { + "delta": 0.4390000104904175, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 3.302999973297119, + "assist": 0.027000000700354576 + }, + { + "sleep": 0.847000002861023, + "assist": 0 + }, + { + "sleep": 3.753999710083008, + "assist": 1.9490000009536743 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-114", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 2, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 3, + "side": 2, + "sleep": 4.461, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 0, + "assist": 0.354, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.879 + }, + { + "kind": "frame", + "delta": 0.81, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.257, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.518, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.901, + "locked": true, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 0.8790000081062317 + }, + { + "step": 5, + "entity": "group2", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.8790000081062317 + }, + { + "sleep": 4.460999965667725, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.3540000021457672 + } + ] + }, + { + "delta": 0.8100000023841858, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.0310001373291016, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.2569999694824219, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.5180000066757202, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.9010000228881836, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-115", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 24, + "side": 2, + "sleep": 2.502, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 21, + "side": 2, + "sleep": 0, + "assist": 3.34, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.289, + "locked": true, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.775, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.959, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.405, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.809, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.614, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.298, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.763, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.003, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.289000004529953, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.502000093460083, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 1.774999976158142, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 2.9590001106262207, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 1.4049999713897705, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 0.8090000152587891, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 2.614000082015991, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 1.2979999780654907, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 1.7630000114440918, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + }, + { + "delta": 0.003000000026077032, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.3399999141693115 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-116", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 3.884, + "assist": 4.393, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.375, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.48, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.996, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.776, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.946 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.55 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": 5.946000099182129 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": 1.5499999523162842 + } + ], + "steps": [ + { + "delta": 0.375, + "updateList": [], + "groups": [ + { + "sleep": 3.884000062942505, + "assist": 4.39300012588501 + } + ] + }, + { + "delta": 0.375, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.39300012588501 + } + ] + }, + { + "delta": 2.4800000190734863, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.39300012588501 + } + ] + }, + { + "delta": 0.9959999918937683, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.39300012588501 + } + ] + }, + { + "delta": 0.7760000228881836, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 4.39300012588501 + } + ] + }, + { + "delta": 0.7760000228881836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.946000099182129 + } + ] + }, + { + "delta": 0.7760000228881836, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.5499999523162842, + "assist": 5.946000099182129 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-117", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 22, + "side": 2, + "sleep": 4.385, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 5, + "side": 1, + "sleep": 1.595, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 18, + "side": 1, + "sleep": 0, + "assist": 1.28, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.657 + }, + { + "kind": "frame", + "delta": 1.662, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.859, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.727, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 6.116 + }, + { + "kind": "frame", + "delta": 2.664, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.889 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 6.6570000648498535 + }, + { + "step": 4, + "entity": "group2", + "floatType": 119, + "value": 6.116000175476074 + }, + { + "step": 6, + "entity": "group1", + "floatType": 119, + "value": 2.8889999389648438 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.6570000648498535, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.2799999713897705 + } + ] + }, + { + "delta": 1.6619999408721924, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.994999408721924, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.2799999713897705 + } + ] + }, + { + "delta": 2.8589999675750732, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.2799999713897705 + } + ] + }, + { + "delta": 0.7269999980926514, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.2799999713897705 + } + ] + }, + { + "delta": 0.7269999980926514, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 6.116000175476074, + "assist": 1.2799999713897705 + } + ] + }, + { + "delta": 2.6640000343322754, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5950000286102295, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.6640000343322754, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.8889999389648438, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-118", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 4.663, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 8, + "side": 2, + "sleep": 3.054, + "assist": 0.719, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 4.688 + }, + { + "kind": "frame", + "delta": 0.744, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 2.054 + }, + { + "kind": "frame", + "delta": 1.873, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 2.956, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.46, + "locked": true, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 4.688000202178955 + }, + { + "step": 2, + "entity": "group1", + "floatType": 13, + "value": 2.053999900817871 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.663000106811523, + "assist": 4.688000202178955 + }, + { + "sleep": 3.053999900817871, + "assist": 0.718999981880188 + } + ] + }, + { + "delta": 0.7440000176429749, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.663000106811523, + "assist": 4.688000202178955 + }, + { + "sleep": 3.053999900817871, + "assist": 0.718999981880188 + } + ] + }, + { + "delta": 0.7440000176429749, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.663000106811523, + "assist": 4.688000202178955 + }, + { + "sleep": 3.053999900817871, + "assist": 2.053999900817871 + } + ] + }, + { + "delta": 1.8730000257492065, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.663000106811523, + "assist": 4.688000202178955 + }, + { + "sleep": 3.053999900817871, + "assist": 2.053999900817871 + } + ] + }, + { + "delta": 2.9560000896453857, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.7069997787475586, + "assist": 1.7319998741149902 + }, + { + "sleep": 0.09799963235855103, + "assist": 0 + } + ] + }, + { + "delta": 1.4600000381469727, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.7069997787475586, + "assist": 1.7319998741149902 + }, + { + "sleep": 0.09799963235855103, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-119", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 5, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 9, + "side": 1, + "sleep": 3.225, + "assist": 1.889, + "onUpdateList": false + }, + { + "subType": 8, + "side": 2, + "sleep": 0.421, + "assist": 0.439, + "onUpdateList": true + }, + { + "subType": 24, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 5.502 + }, + { + "kind": "frame", + "delta": 1.267, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 4.799 + }, + { + "kind": "frame", + "delta": 1.581, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.628, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.328, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group3", + "floatType": 119, + "value": 5.501999855041504 + }, + { + "step": 4, + "entity": "group1", + "floatType": 13, + "value": 4.798999786376953 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group3", + "group0", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2249999046325684, + "assist": 1.8890000581741333 + }, + { + "sleep": 0.42100000381469727, + "assist": 0.4390000104904175 + }, + { + "sleep": 5.501999855041504, + "assist": 0 + } + ] + }, + { + "delta": 1.2669999599456787, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2249999046325684, + "assist": 1.8890000581741333 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.235001564025879, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2249999046325684, + "assist": 1.8890000581741333 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.935001850128174, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2249999046325684, + "assist": 1.8890000581741333 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.935001850128174, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2249999046325684, + "assist": 4.798999786376953 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.935001850128174, + "assist": 0 + } + ] + }, + { + "delta": 1.5809999704360962, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.05600038170814514 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.628000020980835, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.328000068664551, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-120", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 5, + "side": 2, + "sleep": 4.456, + "assist": -0.518, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 6.132 + }, + { + "kind": "frame", + "delta": 1.753, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.272, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1.3, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 6.131999969482422 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.455999851226807, + "assist": 6.131999969482422 + } + ] + }, + { + "delta": 1.753000020980835, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.9499998092651367, + "assist": 2.625999689102173 + } + ] + }, + { + "delta": 1.2719999551773071, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0.9499998092651367, + "assist": 2.625999689102173 + } + ] + }, + { + "delta": 1.2999999523162842, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-121", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 13, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 2, + "side": 1, + "sleep": 2.976, + "assist": 3.309, + "onUpdateList": true + }, + { + "subType": 11, + "side": 1, + "sleep": 4.293, + "assist": 0.177, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.495, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 4.754 + }, + { + "kind": "frame", + "delta": 1.153, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 5.954 + }, + { + "kind": "frame", + "delta": 0.147, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.56 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 1, + "entity": "group1", + "floatType": 13, + "value": 0 + }, + { + "step": 4, + "entity": "group2", + "floatType": 13, + "value": 4.754000186920166 + }, + { + "step": 6, + "entity": "group1", + "floatType": 13, + "value": 5.953999996185303 + }, + { + "step": 8, + "entity": "group0", + "floatType": 119, + "value": 5.559999942779541 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.9760000705718994, + "assist": 3.309000015258789 + }, + { + "sleep": 4.293000221252441, + "assist": 0.1770000010728836 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.9760000705718994, + "assist": 0 + }, + { + "sleep": 4.293000221252441, + "assist": 0.1770000010728836 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.6760003566741943, + "assist": 0 + }, + { + "sleep": 4.293000221252441, + "assist": 0.1770000010728836 + } + ] + }, + { + "delta": 0.4950000047683716, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.6860005855560303, + "assist": 0 + }, + { + "sleep": 4.293000221252441, + "assist": 0.1770000010728836 + } + ] + }, + { + "delta": 0.4950000047683716, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.6860005855560303, + "assist": 0 + }, + { + "sleep": 4.293000221252441, + "assist": 4.754000186920166 + } + ] + }, + { + "delta": 1.152999997138977, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.8339992761611938, + "assist": 1.294999122619629 + } + ] + }, + { + "delta": 1.152999997138977, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.953999996185303 + }, + { + "sleep": 0.8339992761611938, + "assist": 1.294999122619629 + } + ] + }, + { + "delta": 0.1469999998807907, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.660000324249268 + }, + { + "sleep": 0.53999924659729, + "assist": 1.0009992122650146 + } + ] + }, + { + "delta": 0.1469999998807907, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 5.559999942779541, + "assist": 0 + }, + { + "sleep": 0, + "assist": 5.660000324249268 + }, + { + "sleep": 0.53999924659729, + "assist": 1.0009992122650146 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-122", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 16, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 18, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.701 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -2.625 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 1.7009999752044678 + }, + { + "step": 2, + "entity": "group0", + "floatType": 119, + "value": -2.625 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7009999752044678, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.5810000896453857, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": -2.625, + "assist": 0 + }, + { + "sleep": 1.5810000896453857, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-123", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 17, + "side": 2, + "sleep": 3.706, + "assist": 1.715, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.444, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.342, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.027 + }, + { + "kind": "frame", + "delta": 0.626, + "locked": true, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.844, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.022 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 3.0269999504089355 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 3.0220000743865967 + } + ], + "steps": [ + { + "delta": 2.444000005722046, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.34200000762939453, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.34200000762939453, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.0269999504089355 + } + ] + }, + { + "delta": 0.6259999871253967, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.1490001678466797 + } + ] + }, + { + "delta": 2.8440001010894775, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.8440001010894775, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.0220000743865967, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-124", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 1, + "side": 2, + "sleep": 1.017, + "assist": -0.476, + "onUpdateList": false + }, + { + "subType": 11, + "side": 2, + "sleep": 0, + "assist": 3.636, + "onUpdateList": false + }, + { + "subType": 22, + "side": 1, + "sleep": 1.372, + "assist": 4.606, + "onUpdateList": true + }, + { + "subType": 23, + "side": 2, + "sleep": 3.859, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.394, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.379, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 1.3940000534057617, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 1.0169999599456787, + "assist": -0.47600001096725464 + }, + { + "sleep": 0, + "assist": 3.635999917984009 + }, + { + "sleep": 0, + "assist": 3.2119998931884766 + }, + { + "sleep": 3.8589999675750732, + "assist": 0 + } + ] + }, + { + "delta": 0.3790000081062317, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 1.0169999599456787, + "assist": -0.47600001096725464 + }, + { + "sleep": 0, + "assist": 3.635999917984009 + }, + { + "sleep": 0, + "assist": 2.4539995193481445 + }, + { + "sleep": 3.8589999675750732, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-125", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 19, + "side": 2, + "sleep": -0.715, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 6, + "side": 2, + "sleep": 4.662, + "assist": 4.811, + "onUpdateList": false + }, + { + "subType": 18, + "side": 1, + "sleep": 2.203, + "assist": 3.735, + "onUpdateList": true + }, + { + "subType": 14, + "side": 1, + "sleep": 0, + "assist": -1.738, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -2.901 + }, + { + "kind": "frame", + "delta": 0.508, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.784, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.376, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.72 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 6.763 + }, + { + "kind": "frame", + "delta": 2.738, + "locked": true, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": -2.9010000228881836 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 5.71999979019165 + }, + { + "step": 5, + "entity": "group2", + "floatType": 13, + "value": 6.763000011444092 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": -0.7149999737739563, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 2.203000068664551, + "assist": 3.734999895095825 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 0.5080000162124634, + "updateList": [ + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": -0.7149999737739563, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0.6790002584457397, + "assist": 2.210998773574829 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 0.7839999794960022, + "updateList": [ + "group0", + "group2", + "group3" + ], + "groups": [ + { + "sleep": -0.7149999737739563, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0, + "assist": 0.642998456954956 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 2.375999927520752, + "updateList": [ + "group0", + "group3" + ], + "groups": [ + { + "sleep": -0.7149999737739563, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 2.375999927520752, + "updateList": [ + "group0", + "group3" + ], + "groups": [ + { + "sleep": 5.71999979019165, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 2.375999927520752, + "updateList": [ + "group2", + "group0", + "group3" + ], + "groups": [ + { + "sleep": 5.71999979019165, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0, + "assist": 6.763000011444092 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + }, + { + "delta": 2.73799991607666, + "updateList": [ + "group2", + "group0", + "group3" + ], + "groups": [ + { + "sleep": 2.9819998741149902, + "assist": -2.9010000228881836 + }, + { + "sleep": 4.6620001792907715, + "assist": 4.810999870300293 + }, + { + "sleep": 0, + "assist": 4.025000095367432 + }, + { + "sleep": 0, + "assist": -1.7380000352859497 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-126", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 14, + "side": 1, + "sleep": 0, + "assist": 3.605, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.667, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.467, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.6670000553131104, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.6050000190734863 + } + ] + }, + { + "delta": 2.4670000076293945, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.6050000190734863 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-127", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 3.219, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.389, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -1.733 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 119, + "value": -1.7330000400543213 + } + ], + "steps": [ + { + "delta": 0.3889999985694885, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 3.2190001010894775 + } + ] + }, + { + "delta": 0.3889999985694885, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.7330000400543213, + "assist": 3.2190001010894775 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-128", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 11, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.968 + }, + { + "kind": "frame", + "delta": 1.293, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.727, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.897, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.481, + "locked": true, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -2.644 + }, + { + "kind": "frame", + "delta": 1.06, + "locked": false, + "count": 0 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 2.9679999351501465 + }, + { + "step": 5, + "entity": "group0", + "floatType": 13, + "value": -2.6440000534057617 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.9679999351501465 + } + ] + }, + { + "delta": 1.2929999828338623, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.674999713897705 + } + ] + }, + { + "delta": 2.7269999980926514, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.8970000743865967, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4809999465942383, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.4809999465942383, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -2.6440000534057617 + } + ] + }, + { + "delta": 1.059999942779541, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": -2.6440000534057617 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-129", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 10, + "side": 2, + "sleep": 3.586, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 15, + "side": 1, + "sleep": 0, + "assist": 1.771, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 4.885 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.829, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.744, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": -0.854 + }, + { + "kind": "frame", + "delta": 0.999, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.566 + }, + { + "kind": "frame", + "delta": 0.289, + "locked": true, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 4.885000228881836 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 5, + "entity": "group1", + "floatType": 13, + "value": -0.8539999723434448 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": 1.565999984741211 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 3.5859999656677246, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.885000228881836 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 2.836000680923462, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.134998798370361 + } + ] + }, + { + "delta": 0.8289999961853027, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.3490011692047119, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.6480003595352173 + } + ] + }, + { + "delta": 0.8289999961853027, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.3490011692047119, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.6480003595352173 + } + ] + }, + { + "delta": 2.74399995803833, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.3490011692047119, + "assist": 0 + }, + { + "sleep": 0, + "assist": 1.6480003595352173 + } + ] + }, + { + "delta": 2.74399995803833, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.3490011692047119, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.8539999723434448 + } + ] + }, + { + "delta": 0.9990000128746033, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.8539999723434448 + } + ] + }, + { + "delta": 0.9990000128746033, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.565999984741211, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.8539999723434448 + } + ] + }, + { + "delta": 0.289000004529953, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.6989998817443848, + "assist": 0 + }, + { + "sleep": 0, + "assist": -0.8539999723434448 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-130", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 24, + "side": 1, + "sleep": -1.272, + "assist": 2.785, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 4.68 + }, + { + "kind": "frame", + "delta": 0.261, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.474 + }, + { + "kind": "frame", + "delta": 2.77, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 4.679999828338623 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 5.473999977111816 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 4.679999828338623 + } + ] + }, + { + "delta": 0.26100000739097595, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 4.158000469207764 + } + ] + }, + { + "delta": 0.26100000739097595, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 0 + } + ] + }, + { + "delta": 0.26100000739097595, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 5.473999977111816 + } + ] + }, + { + "delta": 2.7699999809265137, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 2.7039990425109863 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.2719999551773071, + "assist": 1.7039988040924072 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-131", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 0, + "side": 2, + "sleep": 0, + "assist": 2.543, + "onUpdateList": false + }, + { + "subType": 16, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 12, + "side": 2, + "sleep": 0.61, + "assist": 4.898, + "onUpdateList": false + }, + { + "subType": 22, + "side": 2, + "sleep": 4.613, + "assist": 2.787, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.542, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.5419999957084656, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.5429999828338623 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.6100000143051147, + "assist": 4.8979997634887695 + }, + { + "sleep": 4.071000576019287, + "assist": 2.244999885559082 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group3" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.5429999828338623 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.6100000143051147, + "assist": 4.8979997634887695 + }, + { + "sleep": 3.821000576019287, + "assist": 1.994999885559082 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-132", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 2, + "side": 1, + "sleep": -1.475, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 7, + "side": 2, + "sleep": 0.288, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.789 + }, + { + "kind": "frame", + "delta": 2.407, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.8, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.683, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.546, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.874, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.858, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0.144 + }, + { + "kind": "frame", + "delta": 0.342, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.317, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 6.789000034332275 + }, + { + "step": 7, + "entity": "group1", + "floatType": 119, + "value": 0.14399999380111694 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 6.789000034332275, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 2.4070000648498535, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 4.381999969482422, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 0.800000011920929, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.7820000648498535, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 0.6830000281333923, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.0989999771118164, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 2.5460000038146973, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 0.8740000128746033, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 1.8580000400543213, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.2879999876022339, + "assist": 0 + } + ] + }, + { + "delta": 1.8580000400543213, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0.14399999380111694, + "assist": 0 + } + ] + }, + { + "delta": 0.34200000762939453, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.31700000166893005, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-133", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 3, + "side": 2, + "sleep": 2.193, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 12, + "side": 1, + "sleep": 3.57, + "assist": 3.518, + "onUpdateList": true + }, + { + "subType": 18, + "side": 2, + "sleep": 3.112, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 15, + "side": 2, + "sleep": 2.568, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.039, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.0390000343322754, + "updateList": [], + "groups": [ + { + "sleep": 2.193000078201294, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.111999988555908, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-134", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 6, + "side": 2, + "sleep": 0, + "assist": 1.442, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 1.355, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.369, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.683, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 2.247, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.929, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.071 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 1.94, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.151, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 0 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": 3.071000099182129 + } + ], + "steps": [ + { + "delta": 1.3550000190734863, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 2.36899995803833, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 1.6829999685287476, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 1.6829999685287476, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 2.246999979019165, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 1.9290000200271606, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 1.9290000200271606, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.071000099182129, + "assist": 1.4420000314712524 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.0709996223449707, + "assist": 0.44200003147125244 + } + ] + }, + { + "delta": 1.940000057220459, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.1509999930858612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-135", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 24, + "side": 2, + "sleep": 4.115, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 2, + "side": 1, + "sleep": -1.499, + "assist": 0.588, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.999 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 1.16 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 7.999000072479248 + }, + { + "step": 1, + "entity": "group1", + "floatType": 119, + "value": 1.159999966621399 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.114999771118164, + "assist": 7.999000072479248 + }, + { + "sleep": -1.4989999532699585, + "assist": 0.5879999995231628 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 4.114999771118164, + "assist": 7.999000072479248 + }, + { + "sleep": 1.159999966621399, + "assist": 0.5879999995231628 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-136", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 3, + "side": 1, + "sleep": 3.465, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.366, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.766 + }, + { + "kind": "frame", + "delta": 1.91, + "locked": true, + "count": 3 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.303 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.841 + }, + { + "kind": "frame", + "delta": 0.517, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 2.7660000324249268 + }, + { + "step": 3, + "entity": "group0", + "floatType": 119, + "value": 5.302999973297119 + }, + { + "step": 4, + "entity": "group0", + "floatType": 119, + "value": 1.840999960899353 + } + ], + "steps": [ + { + "delta": 0.3659999966621399, + "updateList": [], + "groups": [ + { + "sleep": 3.4649999141693115, + "assist": 0 + } + ] + }, + { + "delta": 0.3659999966621399, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.4649999141693115, + "assist": 2.7660000324249268 + } + ] + }, + { + "delta": 1.909999966621399, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.909999966621399, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.302999973297119, + "assist": 0 + } + ] + }, + { + "delta": 1.909999966621399, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.840999960899353, + "assist": 0 + } + ] + }, + { + "delta": 0.5170000195503235, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 1.3239997625350952, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-137", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 25, + "side": 1, + "sleep": 1.176, + "assist": 0.332, + "onUpdateList": false + }, + { + "subType": 21, + "side": 2, + "sleep": 4.938, + "assist": 0.698, + "onUpdateList": false + }, + { + "subType": 22, + "side": 1, + "sleep": 0.328, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 3, + "side": 2, + "sleep": 4.423, + "assist": 0.326, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 5.477 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 5.524 + }, + { + "kind": "frame", + "delta": 0.507, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 1.364 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 5.4770002365112305 + }, + { + "step": 1, + "entity": "group1", + "floatType": 119, + "value": 5.52400016784668 + }, + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 1.3639999628067017 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.1759999990463257, + "assist": 0.3319999873638153 + }, + { + "sleep": 4.938000202178955, + "assist": 5.4770002365112305 + }, + { + "sleep": 0.328000009059906, + "assist": 0 + }, + { + "sleep": 4.422999858856201, + "assist": 0.32600000500679016 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.1759999990463257, + "assist": 0.3319999873638153 + }, + { + "sleep": 5.52400016784668, + "assist": 5.4770002365112305 + }, + { + "sleep": 0.328000009059906, + "assist": 0 + }, + { + "sleep": 4.422999858856201, + "assist": 0.32600000500679016 + } + ] + }, + { + "delta": 0.5070000290870667, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.1759999990463257, + "assist": 0.3319999873638153 + }, + { + "sleep": 5.017000198364258, + "assist": 4.970000267028809 + }, + { + "sleep": 0.328000009059906, + "assist": 0 + }, + { + "sleep": 3.9159998893737793, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.1759999990463257, + "assist": 0.3319999873638153 + }, + { + "sleep": 4.937000274658203, + "assist": 4.890000343322754 + }, + { + "sleep": 0.328000009059906, + "assist": 0 + }, + { + "sleep": 3.8359999656677246, + "assist": 0 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.1759999990463257, + "assist": 1.3639999628067017 + }, + { + "sleep": 4.937000274658203, + "assist": 4.890000343322754 + }, + { + "sleep": 0.328000009059906, + "assist": 0 + }, + { + "sleep": 3.8359999656677246, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-138", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 8, + "groups": [ + { + "subType": 20, + "side": 1, + "sleep": 0.269, + "assist": 2.161, + "onUpdateList": true + }, + { + "subType": 23, + "side": 2, + "sleep": 0, + "assist": 2.256, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 1.033 + }, + { + "kind": "frame", + "delta": 0.25, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 3.875 + }, + { + "kind": "frame", + "delta": 0.649, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.305, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": 1.0329999923706055 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 3.875 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 1.0329999923706055, + "assist": 2.1610000133514404 + }, + { + "sleep": 0, + "assist": 2.25600004196167 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.533000111579895, + "assist": 1.6609998941421509 + }, + { + "sleep": 0, + "assist": 1.7560001611709595 + } + ] + }, + { + "delta": 0.25, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0.533000111579895, + "assist": 3.875 + }, + { + "sleep": 0, + "assist": 1.7560001611709595 + } + ] + }, + { + "delta": 0.6489999890327454, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 3.2259998321533203 + }, + { + "sleep": 0, + "assist": 1.1069999933242798 + } + ] + }, + { + "delta": 0.3050000071525574, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.920999765396118 + }, + { + "sleep": 0, + "assist": 0.8019999265670776 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-139", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 10, + "side": 2, + "sleep": 1.355, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 8, + "side": 1, + "sleep": 3.205, + "assist": 1.572, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.716, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.522 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 5.093 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 2.5220000743865967 + }, + { + "step": 3, + "entity": "group1", + "floatType": 119, + "value": 5.0929999351501465 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 1.3550000190734863, + "assist": 0 + }, + { + "sleep": 3.2049999237060547, + "assist": 1.5720000267028809 + } + ] + }, + { + "delta": 0.7160000205039978, + "updateList": [], + "groups": [ + { + "sleep": 1.3550000190734863, + "assist": 0 + }, + { + "sleep": 3.2049999237060547, + "assist": 1.5720000267028809 + } + ] + }, + { + "delta": 0.7160000205039978, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 1.3550000190734863, + "assist": 0 + }, + { + "sleep": 2.5220000743865967, + "assist": 1.5720000267028809 + } + ] + }, + { + "delta": 0.7160000205039978, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 1.3550000190734863, + "assist": 0 + }, + { + "sleep": 5.0929999351501465, + "assist": 1.5720000267028809 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 1.3550000190734863, + "assist": 0 + }, + { + "sleep": 4.093000411987305, + "assist": 0.5719998478889465 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-140", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 2, + "groups": [ + { + "subType": 13, + "side": 1, + "sleep": 4.579, + "assist": 2.929, + "onUpdateList": true + }, + { + "subType": 1, + "side": 1, + "sleep": 0, + "assist": 3.075, + "onUpdateList": false + }, + { + "subType": 7, + "side": 1, + "sleep": 1.332, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.984, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 2.537, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0.593 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 7.038 + }, + { + "kind": "frame", + "delta": 2.42, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 2.295 + }, + { + "kind": "frame", + "delta": 0.508, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 6.973 + }, + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 2.167 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": -0.843 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 2, + "entity": "group1", + "floatType": 119, + "value": 0.5929999947547913 + }, + { + "step": 3, + "entity": "group2", + "floatType": 119, + "value": 7.038000106811523 + }, + { + "step": 5, + "entity": "group1", + "floatType": 119, + "value": 2.2950000762939453 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": 6.9730000495910645 + }, + { + "step": 8, + "entity": "group1", + "floatType": 13, + "value": 2.1670000553131104 + }, + { + "step": 9, + "entity": "group0", + "floatType": 13, + "value": -0.8429999947547913 + } + ], + "steps": [ + { + "delta": 2.9839999675750732, + "updateList": [ + "group0", + "group2" + ], + "groups": [ + { + "sleep": 4.578999996185303, + "assist": 2.928999900817871 + }, + { + "sleep": 0, + "assist": 3.075000047683716 + }, + { + "sleep": 1.3320000171661377, + "assist": 0 + } + ] + }, + { + "delta": 2.5369999408721924, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.041999340057373, + "assist": 0.3919997215270996 + }, + { + "sleep": 0, + "assist": 3.075000047683716 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5369999408721924, + "updateList": [ + "group1", + "group0" + ], + "groups": [ + { + "sleep": 2.041999340057373, + "assist": 0.3919997215270996 + }, + { + "sleep": 0.5929999947547913, + "assist": 3.075000047683716 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.5369999408721924, + "updateList": [ + "group2", + "group1", + "group0" + ], + "groups": [ + { + "sleep": 2.041999340057373, + "assist": 0.3919997215270996 + }, + { + "sleep": 0.5929999947547913, + "assist": 3.075000047683716 + }, + { + "sleep": 7.038000106811523, + "assist": 0 + } + ] + }, + { + "delta": 2.4200000762939453, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0.6550001502037048 + }, + { + "sleep": 4.617999076843262, + "assist": 0 + } + ] + }, + { + "delta": 2.4200000762939453, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.2950000762939453, + "assist": 0.6550001502037048 + }, + { + "sleep": 4.617999076843262, + "assist": 0 + } + ] + }, + { + "delta": 0.5080000162124634, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 1.7870001792907715, + "assist": 0.14700013399124146 + }, + { + "sleep": 4.10999870300293, + "assist": 0 + } + ] + }, + { + "delta": 0.5080000162124634, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 6.9730000495910645, + "assist": 0 + }, + { + "sleep": 1.7870001792907715, + "assist": 0.14700013399124146 + }, + { + "sleep": 4.10999870300293, + "assist": 0 + } + ] + }, + { + "delta": 0.5080000162124634, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 6.9730000495910645, + "assist": 0 + }, + { + "sleep": 1.7870001792907715, + "assist": 2.1670000553131104 + }, + { + "sleep": 4.10999870300293, + "assist": 0 + } + ] + }, + { + "delta": 0.5080000162124634, + "updateList": [ + "group0", + "group2", + "group1" + ], + "groups": [ + { + "sleep": 6.9730000495910645, + "assist": -0.8429999947547913 + }, + { + "sleep": 1.7870001792907715, + "assist": 2.1670000553131104 + }, + { + "sleep": 4.10999870300293, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-141", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 5, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.991 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0.017 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 2.703 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 5.099 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -1.091 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.666 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.124 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 5.991000175476074 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0.017000000923871994 + }, + { + "step": 4, + "entity": "group0", + "floatType": 13, + "value": 2.703000068664551 + }, + { + "step": 5, + "entity": "group0", + "floatType": 119, + "value": 5.098999977111816 + }, + { + "step": 6, + "entity": "group0", + "floatType": 119, + "value": -1.090999960899353 + }, + { + "step": 7, + "entity": "group0", + "floatType": 13, + "value": 7.665999889373779 + }, + { + "step": 8, + "entity": "group0", + "floatType": 13, + "value": 5.124000072479248 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.991000175476074 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.017000000923871994 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.703000068664551 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 5.098999977111816, + "assist": 2.703000068664551 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.090999960899353, + "assist": 2.703000068664551 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.090999960899353, + "assist": 7.665999889373779 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.090999960899353, + "assist": 5.124000072479248 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.090999960899353, + "assist": 4.1240010261535645 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-142", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 11, + "side": 2, + "sleep": 4.485, + "assist": 0, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": -1.716 + }, + { + "kind": "frame", + "delta": 2.435, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 7.677 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.914, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 2.755, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group0", + "floatType": 119, + "value": -1.715999960899353 + }, + { + "step": 2, + "entity": "group0", + "floatType": 13, + "value": 7.677000045776367 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 0 + } + ] + }, + { + "delta": 2.434999942779541, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 0 + } + ] + }, + { + "delta": 2.434999942779541, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 7.677000045776367 + } + ] + }, + { + "delta": 2.434999942779541, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 0 + } + ] + }, + { + "delta": 0.9139999747276306, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 0 + } + ] + }, + { + "delta": 2.755000114440918, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": -1.715999960899353, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-143", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 20, + "side": 2, + "sleep": 1.087, + "assist": 2.153, + "onUpdateList": false + }, + { + "subType": 16, + "side": 2, + "sleep": 0.157, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 0, + "side": 1, + "sleep": 0, + "assist": 3.614, + "onUpdateList": false + }, + { + "subType": 25, + "side": 1, + "sleep": 0.335, + "assist": 1.162, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.518, + "locked": false, + "count": 0 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.306, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 0.5180000066757202, + "updateList": [ + "group1", + "group3" + ], + "groups": [ + { + "sleep": 1.0870000123977661, + "assist": 2.1530001163482666 + }, + { + "sleep": 0.15700000524520874, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.614000082015991 + }, + { + "sleep": 0.33500000834465027, + "assist": 1.1619999408721924 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 1.0870000123977661, + "assist": 2.1530001163482666 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.614000082015991 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.305999994277954, + "updateList": [], + "groups": [ + { + "sleep": 1.0870000123977661, + "assist": 2.1530001163482666 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 3.614000082015991 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-144", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 23, + "side": 1, + "sleep": 0, + "assist": 0.38, + "onUpdateList": true + }, + { + "subType": 20, + "side": 2, + "sleep": 0, + "assist": 1.688, + "onUpdateList": true + }, + { + "subType": 20, + "side": 1, + "sleep": 4.892, + "assist": 1.913, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 5.927 + }, + { + "kind": "frame", + "delta": 0.04, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1.279, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.975, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.341, + "locked": false, + "count": 2 + }, + { + "kind": "set", + "group": 1, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 1.387, + "locked": true, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 13, + "value": 4.268 + }, + { + "kind": "frame", + "delta": 2.384, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 119, + "value": 5.927000045776367 + }, + { + "step": 5, + "entity": "group1", + "floatType": 119, + "value": 0 + }, + { + "step": 7, + "entity": "group2", + "floatType": 13, + "value": 4.26800012588501 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.3799999952316284 + }, + { + "sleep": 5.927000045776367, + "assist": 1.687999963760376 + }, + { + "sleep": 4.892000198364258, + "assist": 1.9129999876022339 + } + ] + }, + { + "delta": 0.03999999910593033, + "updateList": [ + "group0", + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.2600000202655792 + }, + { + "sleep": 5.807000160217285, + "assist": 1.568000078201294 + }, + { + "sleep": 4.772000312805176, + "assist": 1.7930001020431519 + } + ] + }, + { + "delta": 1.2790000438690186, + "updateList": [ + "group1", + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 3.2490015029907227, + "assist": 0 + }, + { + "sleep": 2.2140016555786133, + "assist": 0 + } + ] + }, + { + "delta": 2.9749999046325684, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.3410000801086426, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.3410000801086426, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3869999647140503, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.3869999647140503, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 4.26800012588501 + } + ] + }, + { + "delta": 2.384000062942505, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-145", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 4, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 0.901, + "onUpdateList": true + }, + { + "subType": 1, + "side": 2, + "sleep": -0.469, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 18, + "side": 1, + "sleep": 0.984, + "assist": 0.771, + "onUpdateList": true + }, + { + "subType": 13, + "side": 1, + "sleep": 0.635, + "assist": 1.544, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.246, + "locked": true, + "count": 2 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.535, + "locked": false, + "count": 0 + }, + { + "kind": "set", + "group": 0, + "floatType": 13, + "value": 5.108 + }, + { + "kind": "frame", + "delta": 0.046, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.719, + "locked": false, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.177, + "locked": true, + "count": 1 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 2 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group0", + "floatType": 13, + "value": 0 + }, + { + "step": 3, + "entity": "group0", + "floatType": 13, + "value": 5.107999801635742 + } + ], + "steps": [ + { + "delta": 2.246000051498413, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.246000051498413, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5350000262260437, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5350000262260437, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.107999801635742 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.04600000008940697, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 5.061999797821045 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.718999981880188, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.6239995956420898 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.1770000010728836, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 1.4469995498657227 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group0", + "group1" + ], + "groups": [ + { + "sleep": 0, + "assist": 0.44699954986572266 + }, + { + "sleep": -0.4690000116825104, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-146", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 1, + "groups": [ + { + "subType": 18, + "side": 2, + "sleep": 0, + "assist": 2.209, + "onUpdateList": false + }, + { + "subType": 20, + "side": 2, + "sleep": 1.199, + "assist": 2.39, + "onUpdateList": false + }, + { + "subType": 5, + "side": 2, + "sleep": 0, + "assist": 1.292, + "onUpdateList": true + }, + { + "subType": 24, + "side": 2, + "sleep": 0, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.958, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 2.711, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 2.911, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 1 + }, + { + "kind": "frame", + "delta": 1.693, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 3, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.1, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group3", + "floatType": 119, + "value": 0 + }, + { + "step": 8, + "entity": "group3", + "floatType": 119, + "value": 0 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 1.2920000553131104 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.9580000042915344, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.7109999656677246, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 2.9110000133514404, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.6929999589920044, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1.6929999589920044, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.10000000149011612, + "updateList": [], + "groups": [ + { + "sleep": 0, + "assist": 2.2090001106262207 + }, + { + "sleep": 1.1990000009536743, + "assist": 2.390000104904175 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-147", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 7, + "groups": [ + { + "subType": 7, + "side": 1, + "sleep": 4.441, + "assist": 2.991, + "onUpdateList": false + }, + { + "subType": 14, + "side": 2, + "sleep": 0, + "assist": 0.344, + "onUpdateList": true + }, + { + "subType": 4, + "side": 1, + "sleep": 3.184, + "assist": 0, + "onUpdateList": false + } + ], + "steps": [ + { + "kind": "set", + "group": 1, + "floatType": 13, + "value": 5.596 + }, + { + "kind": "frame", + "delta": 1.395, + "locked": false, + "count": 3 + }, + { + "kind": "frame", + "delta": 0.683, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 5.817 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 3.818 + }, + { + "kind": "frame", + "delta": 2.976, + "locked": true, + "count": 2 + }, + { + "kind": "frame", + "delta": 0.418, + "locked": false, + "count": 1 + }, + { + "kind": "set", + "group": 0, + "floatType": 119, + "value": 3.061 + }, + { + "kind": "frame", + "delta": 1, + "locked": false, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 0, + "entity": "group1", + "floatType": 13, + "value": 5.5960001945495605 + }, + { + "step": 3, + "entity": "group2", + "floatType": 119, + "value": 5.816999912261963 + }, + { + "step": 4, + "entity": "group2", + "floatType": 119, + "value": 3.818000078201294 + }, + { + "step": 7, + "entity": "group0", + "floatType": 119, + "value": 3.061000108718872 + } + ], + "steps": [ + { + "delta": 0.10000000149011612, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 5.5960001945495605 + }, + { + "sleep": 3.184000015258789, + "assist": 0 + } + ] + }, + { + "delta": 1.3949999809265137, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 1.4109998941421509 + }, + { + "sleep": 3.184000015258789, + "assist": 0 + } + ] + }, + { + "delta": 0.6830000281333923, + "updateList": [ + "group1" + ], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0.7279998064041138 + }, + { + "sleep": 3.184000015258789, + "assist": 0 + } + ] + }, + { + "delta": 0.6830000281333923, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0.7279998064041138 + }, + { + "sleep": 5.816999912261963, + "assist": 0 + } + ] + }, + { + "delta": 0.6830000281333923, + "updateList": [ + "group2", + "group1" + ], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0.7279998064041138 + }, + { + "sleep": 3.818000078201294, + "assist": 0 + } + ] + }, + { + "delta": 2.9760000705718994, + "updateList": [], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.4180000126361847, + "updateList": [], + "groups": [ + { + "sleep": 4.440999984741211, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.4180000126361847, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 3.061000108718872, + "assist": 2.990999937057495 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 1, + "updateList": [ + "group0" + ], + "groups": [ + { + "sleep": 2.061000108718872, + "assist": 1.9909999370574951 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-148", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 6, + "groups": [ + { + "subType": 15, + "side": 1, + "sleep": 4.068, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": false + }, + { + "subType": 13, + "side": 1, + "sleep": 0, + "assist": 1.235, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 0.448, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 0 + }, + { + "kind": "frame", + "delta": 0.782, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 5.864 + }, + { + "kind": "frame", + "delta": 0.5, + "locked": false, + "count": 3 + }, + { + "kind": "set", + "group": 2, + "floatType": 119, + "value": 6.659 + }, + { + "kind": "frame", + "delta": 2.731, + "locked": false, + "count": 3 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [ + { + "step": 1, + "entity": "group2", + "floatType": 119, + "value": 0 + }, + { + "step": 3, + "entity": "group2", + "floatType": 119, + "value": 5.863999843597412 + }, + { + "step": 5, + "entity": "group2", + "floatType": 119, + "value": 6.658999919891357 + } + ], + "steps": [ + { + "delta": 0.4480000138282776, + "updateList": [], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.4480000138282776, + "updateList": [], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7820000052452087, + "updateList": [], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + }, + { + "delta": 0.7820000052452087, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 5.863999843597412, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 4.363999843597412, + "assist": 0 + } + ] + }, + { + "delta": 0.5, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 6.658999919891357, + "assist": 0 + } + ] + }, + { + "delta": 2.7309999465942383, + "updateList": [], + "groups": [ + { + "sleep": 4.067999839782715, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 0, + "assist": 0 + } + ] + } + ] + } + }, + { + "id": "timeline-20260923-149", + "c": "random timeline, outcome recorded from the original C", + "spec": { + "entityUpdateFrameRate": 3, + "groups": [ + { + "subType": 17, + "side": 1, + "sleep": 0, + "assist": -1.047, + "onUpdateList": false + }, + { + "subType": 8, + "side": 1, + "sleep": 0, + "assist": 0, + "onUpdateList": true + }, + { + "subType": 14, + "side": 2, + "sleep": 4.433, + "assist": 4.331, + "onUpdateList": true + } + ], + "steps": [ + { + "kind": "frame", + "delta": 2.11, + "locked": true, + "count": 1 + } + ] + }, + "expected": { + "result": "ok", + "transmissions": [], + "steps": [ + { + "delta": 2.109999895095825, + "updateList": [ + "group2" + ], + "groups": [ + { + "sleep": 0, + "assist": -1.0470000505447388 + }, + { + "sleep": 0, + "assist": 0 + }, + { + "sleep": 2.323000192642212, + "assist": 2.2209999561309814 + } + ] + } + ] + } + } +]; diff --git a/eech-core-ts/test/scenarios/update-timeline.cases.ts b/eech-core-ts/test/scenarios/update-timeline.cases.ts new file mode 100644 index 000000000..d8b4da06d --- /dev/null +++ b/eech-core-ts/test/scenarios/update-timeline.cases.ts @@ -0,0 +1,227 @@ +// +// Behaviour matrix for slice 2: gp_updt.c :: update_server, the gp_float.c +// timer setters, up_update.c :: update_client_server_entities / +// set_entity_update_frame_rate, and time.c :: set_manual_delta_time. +// +// Expected outcomes are derived by reading the original C (the `c` field names +// the branch). Float values follow C float arithmetic, e.g. a 1.0 s frame at +// rate 2 is (int) (1.0f * 2 + 1.0) = 3 passes of 1.0f / 3. They are checked +// against JavaScript, Lua 5.1 and the executed original C. +// +// TSTL-compatible: no Node APIs. +// + +import { EntitySide, EntitySubTypeGroup, FloatType } from "../../src/generated/c-enums"; +import type { TimelineGroupSpec, TimelineOutcome, TimelineSpec, TimelineStep, TimelineStepState } from "./update-timeline"; + +export interface TimelineCase { + id: string; + c: string; + spec: TimelineSpec; + expected: TimelineOutcome; +} + +const SLEEP = FloatType.FLOAT_TYPE_SLEEP; +const ASSIST = FloatType.FLOAT_TYPE_ASSIST_TIMER; + +// C float values used below +const F_0_1 = 0.10000000149011612; // 0.1f: time.c's initial system_delta_time +const F_0_7 = 0.699999988079071; // 0.7f + +function group(sleep: number, assist: number, onUpdateList: boolean): TimelineGroupSpec { + return { subType: EntitySubTypeGroup.ENTITY_SUB_TYPE_GROUP_ATTACK_HELICOPTER, side: EntitySide.ENTITY_SIDE_BLUE_FORCE, sleep, assist, onUpdateList }; +} + +function set(g: number, floatType: FloatType, value: number): TimelineStep { + return { kind: "set", group: g, floatType, value }; +} + +function frame(delta: number, count = 1, locked = false): TimelineStep { + return { kind: "frame", delta, locked, count }; +} + +// timers: [sleep, assist] per group +function state(delta: number, updateList: string[], timers: [number, number][]): TimelineStepState { + const groups = []; + for (const [sleep, assist] of timers) { + groups.push({ sleep, assist }); + } + return { delta, updateList, groups }; +} + +function kase(id: string, c: string, spec: TimelineSpec, expected: Partial): TimelineCase { + return { id, c, spec, expected: { result: "ok", transmissions: [], steps: [], ...expected } }; +} + +function timeline(rate: number, groups: TimelineGroupSpec[], steps: TimelineStep[]): TimelineSpec { + return { entityUpdateFrameRate: rate, groups, steps }; +} + +export const UPDATE_TIMELINE_CASES: TimelineCase[] = [ + // + // gp_float.c :: set_local_float_value (FLOAT_TYPE_SLEEP / FLOAT_TYPE_ASSIST_TIMER) via set_server_float_value + // + kase( + "sleep-set-inserts-and-transmits", + "value != 0.0 && !update_link.parent: insert into LIST_TYPE_UPDATE; server setter transmits", + timeline(2, [group(0, 0, false)], [set(0, SLEEP, 1)]), + { + transmissions: [{ step: 0, entity: "group0", floatType: SLEEP, value: 1 }], + steps: [state(F_0_1, ["group0"], [[1, 0]])], + }, + ), + kase( + "assist-set-inserts-at-head", + "insert_local_entity_into_parents_child_list (.., get_update_entity (), NULL): pred NULL is the head", + timeline(2, [group(1, 0, true), group(0, 0, false)], [set(1, ASSIST, 0.5)]), + { + transmissions: [{ step: 0, entity: "group1", floatType: ASSIST, value: 0.5 }], + steps: [state(F_0_1, ["group1", "group0"], [[1, 0], [0, 0.5]])], + }, + ), + kase( + "set-zero-does-not-insert", + "value == 0.0: no insertion (the value is still set and transmitted)", + timeline(2, [group(0, 0, false)], [set(0, SLEEP, 0)]), + { + transmissions: [{ step: 0, entity: "group0", floatType: SLEEP, value: 0 }], + steps: [state(F_0_1, [], [[0, 0]])], + }, + ), + kase( + "set-negative-inserts", + "value != 0.0 includes negative values", + timeline(2, [group(0, 0, false)], [set(0, SLEEP, -1)]), + { + transmissions: [{ step: 0, entity: "group0", floatType: SLEEP, value: -1 }], + steps: [state(F_0_1, ["group0"], [[-1, 0]])], + }, + ), + kase( + "set-while-on-list-does-not-reinsert", + "update_link.parent already set: no second insertion", + timeline(2, [group(1, 0, true), group(1, 0, true)], [set(1, ASSIST, 2)]), + { + transmissions: [{ step: 0, entity: "group1", floatType: ASSIST, value: 2 }], + steps: [state(F_0_1, ["group0", "group1"], [[1, 0], [1, 2]])], + }, + ), + kase( + "set-narrows-to-float", + "the float parameter narrows 0.1 to 0.1f", + timeline(2, [group(0, 0, false)], [set(0, ASSIST, 0.1)]), + { + transmissions: [{ step: 0, entity: "group0", floatType: ASSIST, value: F_0_1 }], + steps: [state(F_0_1, ["group0"], [[0, F_0_1]])], + }, + ), + + // + // gp_updt.c :: update_server + // + kase( + "sleep-counts-down", + "sleep > 0.0: sleep -= get_delta_time ()", + timeline(2, [group(1, 0, true)], [frame(0.25)]), + { steps: [state(0.25, ["group0"], [[0.75, 0]])] }, + ), + kase( + "expiry-exactly-at-zero-leaves-update-list", + "sleep reaches 0.0 exactly; both timers 0.0: delete_local_entity_from_parents_child_list (LIST_TYPE_UPDATE)", + timeline(1, [group(0.5, 0, true)], [frame(0.5)]), + { steps: [state(0.5, [], [[0, 0]])] }, + ), + kase( + "overshoot-clamps-to-zero", + "max (raw->sleep, 0.0f) clamps a negative result", + timeline(1, [group(0.2, 0, true)], [frame(0.5)]), + { steps: [state(0.5, [], [[0, 0]])] }, + ), + kase( + "both-timers-must-expire", + "sleep expired but assist_timer > 0.0: stays on the update list", + timeline(1, [group(0.2, 1, true)], [frame(0.5), frame(0.5)]), + { steps: [state(0.5, ["group0"], [[0, 0.5]]), state(0.5, [], [[0, 0]])] }, + ), + kase( + "negative-timer-is-never-decremented-and-never-expires", + "sleep < 0.0 is not > 0.0 and not == 0.0: the group stays on the update list", + timeline(1, [group(-1, 0, true)], [frame(1), frame(1)]), + { steps: [state(1, ["group0"], [[-1, 0]]), state(1, ["group0"], [[-1, 0]])] }, + ), + kase( + "zero-timers-on-list-leave-on-first-update", + "a restored group on the list with both timers 0.0 is removed by its first update", + timeline(2, [group(0, 0, true)], [frame(0.1)]), + { steps: [state(F_0_1, [], [[0, 0]])] }, + ), + kase( + "self-removal-does-not-break-the-walk", + "update_succ is saved before update_client_server_entity (en), so the successor is still updated", + timeline(1, [group(0.1, 0, true), group(1, 0, true)], [frame(0.5)]), + { steps: [state(0.5, ["group1"], [[0, 0], [0.5, 0]])] }, + ), + + // + // up_update.c :: update_client_server_entities / set_entity_update_frame_rate, time.c :: set_manual_delta_time + // + kase( + "frame-is-subdivided-into-equal-float-sub-steps", + "iterations = (int) (1.0f * 2 + 1.0) = 3; each pass subtracts 1.0f / 3 = 0.33333334f", + timeline(2, [group(1, 0, true)], [frame(1)]), + // 1 - 0.33333334 = 0.6666666; - 0.33333334 = 0.33333328; - 0.33333334 < 0 -> clamped to 0 and removed + { steps: [state(1, [], [[0, 0]])] }, + ), + kase( + "sub-step-float-rounding", + "0.7f at rate 4: 3 passes of 0.23333333f; the frame delta is restored afterwards", + timeline(4, [group(0, 2, true)], [frame(0.7)]), + { steps: [state(F_0_7, ["group0"], [[0, 1.2999999523162842]])] }, + ), + kase( + "time-acceleration-repeats-the-update", + "the host calls update_client_server_entities () once per time-acceleration step", + timeline(2, [group(1, 0, true)], [frame(0.25, 3)]), + { steps: [state(0.25, ["group0"], [[0.25, 0]])] }, + ), + kase( + "paused-frame-updates-nothing", + "time acceleration 0: update_client_server_entities () is not called; the frame delta is still measured", + timeline(2, [group(1, 0, true)], [frame(0.5, 0)]), + { steps: [state(0.5, ["group0"], [[1, 0]])] }, + ), + kase( + "locked-frame-rate-is-not-subdivided", + "locked_frame_rate: one pass with the whole delta; set_manual_delta_time is refused", + timeline(2, [group(2, 0, true)], [frame(1, 1, true)]), + { steps: [state(1, ["group0"], [[1, 0]])] }, + ), + kase( + "frame-rate-below-range-asserts", + "set_entity_update_frame_rate: ASSERT ((frame_rate >= 1) && (frame_rate <= 100))", + timeline(0, [group(1, 0, true)], [set(0, SLEEP, 2), frame(0.5)]), + { + result: "assert:(frame_rate >= 1) && (frame_rate <= 100)", + transmissions: [{ step: 0, entity: "group0", floatType: SLEEP, value: 2 }], + steps: [state(F_0_1, ["group0"], [[2, 0]])], + }, + ), + kase( + "frame-rate-above-range-asserts", + "set_entity_update_frame_rate: ASSERT ((frame_rate >= 1) && (frame_rate <= 100))", + timeline(101, [group(1, 0, true)], [frame(0.5)]), + { result: "assert:(frame_rate >= 1) && (frame_rate <= 100)" }, + ), + kase( + "frame-rate-not-used-when-locked", + "locked_frame_rate skips set_entity_update_frame_rate, so an out-of-range rate is not asserted", + timeline(0, [group(1, 0, true)], [frame(0.5, 1, true)]), + { steps: [state(0.5, ["group0"], [[0.5, 0]])] }, + ), + kase( + "frame-rate-not-used-when-paused", + "no update call, no frame rate assertion", + timeline(101, [group(1, 0, true)], [frame(0.5, 0)]), + { steps: [state(0.5, ["group0"], [[1, 0]])] }, + ), +]; diff --git a/eech-core-ts/test/scenarios/update-timeline.ts b/eech-core-ts/test/scenarios/update-timeline.ts new file mode 100644 index 000000000..4d596d75e --- /dev/null +++ b/eech-core-ts/test/scenarios/update-timeline.ts @@ -0,0 +1,209 @@ +// +// Update timeline scenarios (slice 2), shared by every conformance runner: +// vitest, the Lua 5.1 runner and the C reference harness (serialiseTimeline). +// +// A timeline restores groups (raw timers and update-list membership, as a +// saved campaign holds them), then runs steps: +// +// set: set_client_server_entity_float_value (group, FLOAT_TYPE_SLEEP | +// FLOAT_TYPE_ASSIST_TIMER, value): the real server setter +// frame: the host flight loop (flight.c): the Clock supplies the frame's +// delta and locked flag (set_delta_time), then the real +// update_client_server_entities () runs `count` times +// (time acceleration; 0 = paused) +// +// The frame driver is the environment. It decides nothing about what time +// does; the ported update loop and group update function do. +// +// TSTL-compatible: no Node APIs, no Map/Set, no JSON, no undefined properties. +// + +import { EechAssertionError } from "../../src/core/assert"; +import { toFloat32 } from "../../src/core/float32"; +import { getDeltaTime } from "../../src/core/time"; +import { initialiseCampaignCore, setDeltaTime } from "../../src"; +import type { GroupRaw } from "../../src/entity/special/group/group"; +import { setUpdateEntity, updateClientServerEntities } from "../../src/entity/special/update/update"; +import { getLocalEntityChildSucc, getLocalEntityFirstChild, insertLocalEntityIntoParentsChildListRaw } from "../../src/entity/system/en_list"; +import { getLocalEntityFloatValue, setClientServerEntityFloatValue } from "../../src/entity/system/en_values"; +import { createLocalEntityRaw, type Entity } from "../../src/entity/system/entity"; +import { EntitySide, EntitySubTypeGroup, EntityType, FloatType, ListType } from "../../src/generated/c-enums"; +import { InMemoryMobilePhysicalState } from "../adapters/in-memory-mobile-physical-state"; +import { RecordingEntityReplication } from "../adapters/recording-entity-replication"; +import { ScriptedClock } from "../adapters/scripted-clock"; + +export interface TimelineGroupSpec { + subType: EntitySubTypeGroup; + side: EntitySide; + sleep: number; + assist: number; + // restored on LIST_TYPE_UPDATE (in spec order) + onUpdateList: boolean; +} + +export type TimelineStep = + | { kind: "set"; group: number; floatType: FloatType; value: number } + | { kind: "frame"; delta: number; locked: boolean; count: number }; + +export interface TimelineSpec { + entityUpdateFrameRate: number; + groups: TimelineGroupSpec[]; + steps: TimelineStep[]; +} + +export interface TimelineTransmit { + step: number; + entity: string; + floatType: number; + value: number; +} + +export interface TimelineGroupState { + sleep: number; + assist: number; +} + +export interface TimelineStepState { + delta: number; + // labels of LIST_TYPE_UPDATE in list order + updateList: string[]; + groups: TimelineGroupState[]; +} + +export interface TimelineOutcome { + // "ok" or "assert:" + result: string; + transmissions: TimelineTransmit[]; + // one entry per completed step + steps: TimelineStepState[]; +} + +// Runaway guard (test protection, not campaign policy): a frame may not ask +// for more update passes than this; see validateTimeline. +export const MAX_UPDATE_PASSES_PER_FRAME = 10000; + +export function validateTimeline(spec: TimelineSpec): void { + for (const step of spec.steps) { + if (step.kind === "frame") { + const passes = step.count * (step.delta * spec.entityUpdateFrameRate + 1); + if (!(step.delta > 0) || step.count < 0 || passes > MAX_UPDATE_PASSES_PER_FRAME) { + throw new Error(`timeline frame outside the test bounds (delta ${step.delta}, count ${step.count})`); + } + } + } +} + +export function runTimeline(spec: TimelineSpec): TimelineOutcome { + validateTimeline(spec); + + const replication = new RecordingEntityReplication(); + const clock = new ScriptedClock(); + + initialiseCampaignCore( + { mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: replication, clock }, + { unportedMessagePolicy: "record", entityUpdateFrameRate: spec.entityUpdateFrameRate }, + ); + + const labels: Record = {}; + + const update = createLocalEntityRaw(EntityType.ENTITY_TYPE_UPDATE, {}); + + setUpdateEntity(update); + + const groups: Entity[] = []; + + let tail: Entity | undefined = undefined; + + for (let i = 0; i < spec.groups.length; i++) { + const g = spec.groups[i]; + const raw: GroupRaw = { + sub_type: g.subType, + side: g.side, + supplies: { ammo_supply_level: 0, fuel_supply_level: 0 }, + sleep: toFloat32(g.sleep), + assist_timer: toFloat32(g.assist), + }; + const group = createLocalEntityRaw(EntityType.ENTITY_TYPE_GROUP, raw); + labels[group.index] = `group${i}`; + if (g.onUpdateList) { + insertLocalEntityIntoParentsChildListRaw(group, ListType.LIST_TYPE_UPDATE, update, tail); + tail = group; + } + groups.push(group); + } + + const outcome: TimelineOutcome = { result: "ok", transmissions: [], steps: [] }; + + const collectTransmissions = (step: number): void => { + for (const t of replication.transmissions.splice(0, replication.transmissions.length)) { + outcome.transmissions.push({ step, entity: labels[t.entityIndex], floatType: t.type, value: t.value }); + } + }; + + for (let s = 0; s < spec.steps.length; s++) { + const step = spec.steps[s]; + + try { + if (step.kind === "set") { + setClientServerEntityFloatValue(groups[step.group], step.floatType, step.value); + } else { + clock.setFrame(step.delta, step.locked); + + setDeltaTime(); + + for (let c = 0; c < step.count; c++) { + updateClientServerEntities(); + } + } + } catch (e) { + if (e instanceof EechAssertionError) { + collectTransmissions(s); + outcome.result = `assert:${e.expression}`; + return outcome; + } + throw e; + } + + collectTransmissions(s); + + const updateList: string[] = []; + let en = getLocalEntityFirstChild(update, ListType.LIST_TYPE_UPDATE); + while (en) { + updateList.push(labels[en.index]); + en = getLocalEntityChildSucc(en, ListType.LIST_TYPE_UPDATE); + } + + const states: TimelineGroupState[] = []; + for (const group of groups) { + states.push({ + sleep: getLocalEntityFloatValue(group, FloatType.FLOAT_TYPE_SLEEP), + assist: getLocalEntityFloatValue(group, FloatType.FLOAT_TYPE_ASSIST_TIMER), + }); + } + + outcome.steps.push({ delta: getDeltaTime(), updateList, groups: states }); + } + + return outcome; +} + +// Input for c-reference/harness.c (timeline mode). +export function serialiseTimeline(spec: TimelineSpec, formatNumber: (n: number) => string): string { + const lines: string[] = [`timeline ${spec.entityUpdateFrameRate}`]; + + for (const g of spec.groups) { + lines.push(`tgroup ${g.subType} ${g.side} ${formatNumber(g.sleep)} ${formatNumber(g.assist)} ${g.onUpdateList ? 1 : 0}`); + } + + for (const step of spec.steps) { + if (step.kind === "set") { + lines.push(`set ${step.group} ${step.floatType} ${formatNumber(step.value)}`); + } else { + lines.push(`frame ${formatNumber(step.delta)} ${step.locked ? 1 : 0} ${step.count}`); + } + } + + lines.push("end"); + + return lines.join("\n") + "\n"; +} diff --git a/eech-core-ts/test/unit/entity-runtime.test.ts b/eech-core-ts/test/unit/entity-runtime.test.ts index 7236c1596..c2df67fd8 100644 --- a/eech-core-ts/test/unit/entity-runtime.test.ts +++ b/eech-core-ts/test/unit/entity-runtime.test.ts @@ -15,9 +15,10 @@ import { createLocalEntityRaw, deinitialiseEntityRuntime, getCampaignPorts } fro import { CommsModelType, EntityMessage, EntitySide, EntitySubTypeCargo, EntityType, FloatType, IntType, ListType } from "../../src/generated/c-enums"; import { InMemoryMobilePhysicalState } from "../adapters/in-memory-mobile-physical-state"; import { RecordingEntityReplication } from "../adapters/recording-entity-replication"; +import { ScriptedClock } from "../adapters/scripted-clock"; function ports() { - return { mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: new RecordingEntityReplication() }; + return { mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: new RecordingEntityReplication(), clock: new ScriptedClock() }; } afterEach(() => { @@ -103,7 +104,7 @@ describe("entity runtime", () => { initialiseCampaignCore(ports()); setCommsModel(CommsModelType.COMMS_MODEL_CLIENT); const replication = new RecordingEntityReplication(); - initialiseCampaignCore({ mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: replication }); + initialiseCampaignCore({ mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: replication, clock: new ScriptedClock() }); const keysite = createLocalEntityRaw(EntityType.ENTITY_TYPE_KEYSITE, { supplies: { ammo_supply_level: 0, fuel_supply_level: 0 } }); setClientServerEntityFloatValue(keysite, FloatType.FLOAT_TYPE_AMMO_SUPPLY_LEVEL, 0.1); // the C prototype's float parameter narrows the value diff --git a/eech-core-ts/test/unit/update-runtime.test.ts b/eech-core-ts/test/unit/update-runtime.test.ts new file mode 100644 index 000000000..a4432bab7 --- /dev/null +++ b/eech-core-ts/test/unit/update-runtime.test.ts @@ -0,0 +1,138 @@ +// +// Isolated tests of runtime primitives that the adopted group behaviour does +// not reach by itself. Expectations come from the quoted C. +// + +import { afterEach, describe, expect, it } from "vitest"; +import { initialiseCampaignCore } from "../../src"; +import { EechFatalError, UnportedBehaviourError } from "../../src/core/assert"; +import { getDeltaTime, setDeltaTimeFrom, setManualDeltaTime } from "../../src/core/time"; +import { getUpdateSucc, setEntityUpdateFrameRate, setUpdateEntity, setUpdateSucc } from "../../src/entity/special/update/update"; +import { + deleteLocalEntityFromParentsChildList, + getLocalEntityChildPred, + getLocalEntityFirstChild, + insertLocalEntityIntoParentsChildList, + insertLocalEntityIntoParentsChildListRaw, +} from "../../src/entity/system/en_list"; +import { notifyLocalEntity } from "../../src/entity/system/en_msgs"; +import { createLocalEntityRaw, deinitialiseEntityRuntime, type Entity } from "../../src/entity/system/entity"; +import { EntityMessage, EntityType, ListType } from "../../src/generated/c-enums"; +import { InMemoryMobilePhysicalState } from "../adapters/in-memory-mobile-physical-state"; +import { RecordingEntityReplication } from "../adapters/recording-entity-replication"; +import { ScriptedClock } from "../adapters/scripted-clock"; + +function start(): { update: Entity; groups: Entity[] } { + initialiseCampaignCore({ mobilePhysicalState: new InMemoryMobilePhysicalState(), entityReplication: new RecordingEntityReplication(), clock: new ScriptedClock() }); + const update = createLocalEntityRaw(EntityType.ENTITY_TYPE_UPDATE, {}); + setUpdateEntity(update); + const groups: Entity[] = []; + for (let i = 0; i < 3; i++) { + groups.push(createLocalEntityRaw(EntityType.ENTITY_TYPE_GROUP, { sleep: 0, assist_timer: 0 })); + } + return { update, groups }; +} + +afterEach(() => { + deinitialiseEntityRuntime(); +}); + +describe("time.c :: set_manual_delta_time", () => { + it("is refused while the frame rate is locked", () => { + start(); + const clock = new ScriptedClock(); + clock.setFrame(0.5, true); + setDeltaTimeFrom(clock); + setManualDeltaTime(0.25); + expect(getDeltaTime()).toBe(0.5); + clock.setFrame(0.5, false); + setDeltaTimeFrom(clock); + setManualDeltaTime(0.25); + expect(getDeltaTime()).toBe(0.25); + }); +}); + +describe("up_update.c :: set_entity_update_frame_rate", () => { + it("truncates towards zero like a C (int) cast", () => { + start(); + const clock = new ScriptedClock(); + // time.c never measures a negative delta; the cast semantics are still C's: + // (int) (-1.25f * 2 + 1.0) = (int) -1.5 = -1, and -1.25f / -1 = 1.25f + clock.setFrame(-1.25, false); + setDeltaTimeFrom(clock); + expect(setEntityUpdateFrameRate(2)).toBe(-1); + expect(getDeltaTime()).toBe(1.25); + // (int) (0.3f * 2 + 1.0) = (int) 1.6000000238418579 = 1 + clock.setFrame(0.3, false); + setDeltaTimeFrom(clock); + expect(setEntityUpdateFrameRate(2)).toBe(1); + }); +}); + +describe("en_list.c :: insert / delete with notifications", () => { + it("inserts after a predecessor and deletes from the middle", () => { + const { update, groups } = start(); + const [a, b, c] = groups; + insertLocalEntityIntoParentsChildList(a, ListType.LIST_TYPE_UPDATE, update, undefined); + insertLocalEntityIntoParentsChildList(c, ListType.LIST_TYPE_UPDATE, update, a); + insertLocalEntityIntoParentsChildList(b, ListType.LIST_TYPE_UPDATE, update, a); + expect(getLocalEntityChildPred(c, ListType.LIST_TYPE_UPDATE)).toBe(b); + deleteLocalEntityFromParentsChildList(b, ListType.LIST_TYPE_UPDATE); + expect(getLocalEntityChildPred(c, ListType.LIST_TYPE_UPDATE)).toBe(a); + deleteLocalEntityFromParentsChildList(a, ListType.LIST_TYPE_UPDATE); + expect(getLocalEntityFirstChild(update, ListType.LIST_TYPE_UPDATE)).toBe(c); + expect(getLocalEntityChildPred(c, ListType.LIST_TYPE_UPDATE)).toBeUndefined(); + }); + + it("deleting an entity that is in no list does nothing", () => { + const { update, groups } = start(); + deleteLocalEntityFromParentsChildList(groups[0], ListType.LIST_TYPE_UPDATE); + expect(getLocalEntityFirstChild(update, ListType.LIST_TYPE_UPDATE)).toBeUndefined(); + }); + + it("#ifdef DEBUG: inserting an entity already in the list is fatal", () => { + const { update, groups } = start(); + insertLocalEntityIntoParentsChildList(groups[0], ListType.LIST_TYPE_UPDATE, update, undefined); + insertLocalEntityIntoParentsChildList(groups[1], ListType.LIST_TYPE_UPDATE, update, undefined); + expect(() => insertLocalEntityIntoParentsChildList(groups[0], ListType.LIST_TYPE_UPDATE, update, undefined)).toThrow( + new EechFatalError("Entity already in list (entity type = ENTITY_TYPE_GROUP, list type = LIST_TYPE_UPDATE)"), + ); + }); + + it("an entity whose recorded parent no longer lists it may be inserted (the C check walks the list)", () => { + const { update, groups } = start(); + // a stale parent pointer, as en_list.c notes can happen with ENTITY_ATTR_PARENT + groups[0].links.update_link = { parent: update, child_succ: undefined, child_pred: undefined }; + insertLocalEntityIntoParentsChildList(groups[0], ListType.LIST_TYPE_UPDATE, update, undefined); + expect(getLocalEntityFirstChild(update, ListType.LIST_TYPE_UPDATE)).toBe(groups[0]); + }); +}); + +describe("up_msgs.c :: response_to_unlink_child", () => { + it("advances update_succ when the pending successor is unlinked", () => { + const { update, groups } = start(); + const [a, b, c] = groups; + insertLocalEntityIntoParentsChildListRaw(a, ListType.LIST_TYPE_UPDATE, update, undefined); + insertLocalEntityIntoParentsChildListRaw(b, ListType.LIST_TYPE_UPDATE, update, a); + insertLocalEntityIntoParentsChildListRaw(c, ListType.LIST_TYPE_UPDATE, update, b); + setUpdateSucc(b); + deleteLocalEntityFromParentsChildList(b, ListType.LIST_TYPE_UPDATE); + expect(getUpdateSucc()).toBe(c); + deleteLocalEntityFromParentsChildList(a, ListType.LIST_TYPE_UPDATE); + expect(getUpdateSucc()).toBe(c); + }); + + it("ignores other list types", () => { + const { update, groups } = start(); + setUpdateSucc(groups[0]); + expect(notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_UNLINK_CHILD, update, groups[0], ListType.LIST_TYPE_MEMBER)).toBe(1); + expect(getUpdateSucc()).toBe(groups[0]); + }); +}); + +describe("gp_msgs.c :: response_to_link_parent", () => { + it("LIST_TYPE_DIVISION (set_local_division_name) is not ported", () => { + const { update, groups } = start(); + expect(() => notifyLocalEntity(EntityMessage.ENTITY_MESSAGE_LINK_PARENT, groups[0], update, ListType.LIST_TYPE_DIVISION)).toThrow(UnportedBehaviourError); + }); +}); diff --git a/eech-core-ts/test/unit/update-timeline.test.ts b/eech-core-ts/test/unit/update-timeline.test.ts new file mode 100644 index 000000000..e70727af7 --- /dev/null +++ b/eech-core-ts/test/unit/update-timeline.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest"; +import { UPDATE_TIMELINE_CASES } from "../scenarios/update-timeline.cases"; +import { runTimeline } from "../scenarios/update-timeline"; +import { C_REFERENCE_RANDOM_TIMELINES } from "../scenarios/generated/c-reference-random-timelines.cases"; + +describe("update timeline conformance (JavaScript)", () => { + for (const c of UPDATE_TIMELINE_CASES) { + it(`${c.id} [${c.c}]`, () => { + expect(runTimeline(c.spec)).toEqual(c.expected); + }); + } +}); + +describe("random timelines recorded from the original C (JavaScript)", () => { + it(`all ${C_REFERENCE_RANDOM_TIMELINES.length} match`, () => { + for (const c of C_REFERENCE_RANDOM_TIMELINES) { + expect(runTimeline(c.spec), c.id).toEqual(c.expected); + } + }); +});