# WILDROOT engine fixes, loop progress
Source: `/home/vasylt/Code/rusting/RustingGames/prompts/wildroot/ENGINE_FIXES_LOOP.md`.
Problem details: `/home/vasylt/Code/rusting/RustingGames/wildroot/FRICTION.md`.
- [x] 0. Check the earlier work (full test suite, then gpu-tests through the lock)
- Evidence: `cargo test --workspace` 1072 lib + 19 doc tests pass; gpu-tests run through the lock: 1246 lib tests pass, doctests failed once with "can't find crate" from a concurrent build and pass on rerun (19). Also fixed the Windows build (`src/project.rs` zip mode literal had no type).
- [x] 1. F19: fixed-tick hook `FrameTime::delta_seconds()` returns frame time
- Evidence: `a_tick_function_steps_by_the_fixed_delta_whatever_the_frame_time`: `delta_seconds()` in a tick was the frame time (2.5 steps), now the fixed step; `Update` still sees the frame time.
- [x] 2. F26: cooldowns share names with counters
- Evidence: `a_cooldown_and_a_counter_of_the_same_name_are_separate`; cooldown end tick now in counter `cooldown:<name>` (`cooldowns_count_fixed_ticks`).
- [x] 3. F25: shutdown hook (`scene.exiting()`) and atomic `save_data`
- Evidence: `the_exit_update_runs_once_with_exiting_set_and_no_tick`, `a_dedicated_server_ticks_in_real_time_until_the_game_quits` (EXITS == 1), `sigterm_gives_a_dedicated_server_one_last_update_then_stops`; Windows target checks. `save_data` was already temp + rename. Headless `--ticks`/scenario runs get no exit update, so scenario hashes stay the same.
- [x] 4. F20: per-run scenario report path
- Evidence: `two_runs_at_once_each_read_their_own_scenario_report`: two overlapping runs each report their own scenario; `build/` keeps only `scenario-report.json` (the last finished run).
- [x] 5. F21: session handshake carries the host's world seed
- Evidence: `a_joining_client_receives_the_hosts_world_seed` (direct, relay, loopback, WELCOME parse); new `NetSession::set_seed`/`seed`, `GameScene::set_seed`; protocol version 3 -> 4.
- [x] 6. F17 + F18 + F2: `api/` docs for `net` and `procgen`, `Smoothing::correct` note
- Evidence: `net_and_procgen_items_have_api_pages` (`api/PeerId`, `api/fbm2`, `api/NetSession`, `api/net::predict`, ...; before: "no docs item"). `api/Smoothing::correct` and the guide example pass `smoothing.shown(position, Duration::ZERO)` instead of last frame's drawn position.
- [x] 7. F18 (code): `Interpolation` over plain `(tick, position)` samples — `net::interpolate::Track` (push/at/newest); test `a_track_blends_plain_tick_samples_that_arrive_out_of_order` passes, fmt + clippy ×3 clean.
- [x] 8. F23: terrain-wide `tint` and `roughness` — `Terrain.tint`/`roughness` update the material only (shape hash skips rebuild), `GameScene::set_terrain_look`, shader tints blended layers; `terrain_assets_rebuild_on_change_and_free_on_removal` + GPU `terrain_paints_splat_layers…` (quarter tint darkens) pass, fmt + clippy ×3 clean.
- [x] 9. F24: `asset import --linear`, reimport recooks glTF textures, warn on edited copy — `--linear` cooks `<name>-linear.rtexture`, plain images in normal/MR/occlusion slots load linear (`load_linear_texture`), glTF reimport recooks `.rtexture` at the copy, edited copy kept with `ASSET_EDITED_COPY`; `linear_images_cook_a_linear_texture_and_edited_copies_survive` + extended gltf reimport test pass (154 tests), fmt + clippy ×3 clean.
- [x] 10. F22: realtime scenario tick timing independent of capture time — realtime scenarios pin `net::pin_clock` to each tick's planned time, so `NetSession::simulate` delays count in tick time, not frame cost; `a_pinned_clock_times_simulated_delays` fails without the fix and passes, 252 net/scenario/docs tests pass, fmt + clippy ×3 clean. WILDROOT rerun skipped (its capture scenario writes into the game folder).
- [x] 11. F12: foliage `conform: true` and `layers: [..]` — `Foliage.conform`/`layers`, `terrain::ground` samples the drawn triangles and splat blend, heightfield changes re-place copies; `conforming_foliage_sits_on_the_ground_of_its_chosen_layers` passes (fails without: copies stay at y 10, all 200 drawn), foliage GPU + docs/schema/reflect tests pass, fmt + clippy ×3 clean.
- [x] 12. F13: standard material `sway` block — `MaterialAsset.sway` / scene `material.Inline.sway` (`MaterialSway { amplitude, frequency, height }`), cooked format v11 with v10 legacy decode, vertex + shadow shaders bend via `foliage_lean`/`sway_bend` on set 0 binding 14, fields upload whenever physics exists; `swaying_materials_bend_plain_meshes_in_wind_fields` (GPU) and `version_ten_cooked_scenes_load_without_sway_and_sway_round_trips` pass (GPU test fails without: plain meshes stay upright), fmt + clippy ×3 clean, workspace tests pass after registering `ASSET_EDITED_COPY` (F24 leftover).
- [x] 13. F7: water `deep_color`, `shore_foam`, scrolling detail normal — `WaterBody.{deep_color, depth_fade, shore_foam, ripples, ripple_length}` map to `MaterialAsset.water` (`WaterShading`); scene.frag water branch (transmission.w = 2) reads the opaque depth via `inverse_view_projection`, renderer copies depth when `water_depth`; `water_takes_its_deep_color_and_shore_foam_from_the_scene_depth` (GPU, fails without: `deep: 124 124 124`) and `water_options_default_to_a_plain_surface_and_map_to_the_material` pass, terrain/splat/refract/reflection/water GPU filters green, workspace tests 1079 pass, fmt + clippy ×3 clean.
- [x] 14. F8: glTF `KHR_texture_transform` folding — enabled the existing gltf crate's `KHR_texture_transform` feature (no new crate); `gltf_uv_transform` + `transform_uv` fold the shared offset/rotation/scale into TEXCOORD_0 in `import_gltf`, differing transforms fail naming the material; `gltf_import_folds_a_shared_texture_transform_into_the_uvs` passes (fails without: `Unsupported extension`), gltf tests 10 pass, fmt + clippy ×3 clean.
- [x] 15. F16: pure kinematic character step, used by `rusting.player_controller` — `PlayerController::step` + `PlayerInput`/`PlayerShove`; `player_step_replays_the_controller_from_an_input_without_global_actions` passes, 62 player/docs tests green, clippy ×3 clean
- [x] 16. F9: `rusting asset bake-foliage` — new `foliage_bake` module + CLI (`--cards --impostor --node --tint --atlas --out`); 3 foliage_bake tests pass; Poly Haven fir_tree_01 node a: 4,066,119 foliage tris -> 6000 card tris + 4-tri impostor; clippy ×3 clean
- [x] 17. F1: `asset import` cooks `.rmesh`/`.rlod`/`.rtexture`, fills `.rmeta` deps — cooking itself landed with F24 (`cook()` imports at the copy); new `.rmeta` `cooked` list + report `meshes` (`CookedMesh { mesh, triangles, lods }`), human `Mesh: ... triangles, LODs a/b/c` line; `gltf_import_records_cooked_meshes_and_lods` passes, 6 gltf_import tests green, fmt + clippy ×3 clean.