Codebase Map
The quickest way to find where a change belongs is to follow one frame of data through the system: from a camera packet to a robot command. The map below is organised along that path, not along the crate dependency graph.
flowchart TD
VIS[SSL Vision / Game Controller<br/>dies-ssl-client]
SIM[Simulator + auto-ref<br/>dies-simulator]
BS[Robot feedback<br/>dies-basestation-client]
TR[World tracking<br/>dies-world]
EX[Executor frame tick<br/>dies-executor]
STRAT[Strategy process<br/>StrategyHost - IPC - Strategy::update]
subgraph ctrl[TeamController, per robot]
SK[Skills] --> COMP[Rule compliance] --> PLAN[Path planning + ORCA] --> PC[PlayerController]
end
OUT[Robot commands<br/>basestation / simulator]
UI[Web UI<br/>dies-webui + webui/]
LOG[Binary log<br/>dies-logger]
VIS --> TR
SIM --> TR
BS --> TR
TR -- WorldData --> EX
EX -- WorldSnapshot --> STRAT
STRAT -- SkillCommands --> SK
EX -- WorldUpdate --> UI
UI -. ControlMsg .-> EX
EX --> LOG
PC -- PlayerCmd --> OUT
One frame, step by step
Everything is driven by a vision frame. In sim the loop steps the simulator and feeds its output through exactly the same entry points as real packets.
| # | Stage | Where | What happens |
|---|---|---|---|
| 1 | Receive | dies-ssl-client, dies-basestation-client, dies-simulator | Vision/referee packets arrive over UDP (or TCP); robot feedback (breakbeam, voltages, …) over the serial basestation. The simulator produces the same messages (detection, gc_message, feedback). |
| 2 | Dispatch | Executor (run_rt_live, run_rt_sim, step_simulation, run_headless) | The main loop in dies-executor selects on all inputs and calls update_from_vision_msg / update_from_gc_msg / update_from_bs_msg. |
| 3 | Track | WorldTracker | Kalman-filtered players and ball (filters), game state from referee commands, and possession. WorldTracker::get() yields a WorldData in absolute coordinates. |
| 4 | Frame tick | Executor::update_team_controller | The heart of the system. Runs the steps below in order, once per vision frame. |
| 4a | Publish | same | Broadcasts a WorldUpdate to the web UI (absolute coordinates). |
| 4b | To team frame | WorldData::get_team_data, sides.rs | Converts to a team-relative frame (+x always toward the opponent goal). Everything downstream of this point is team-relative. |
| 4c | Strategy | StrategyHost → transform.rs → IPC → runner → Strategy::update | Builds a WorldSnapshot, sends it to each team's strategy process, and collects SkillCommands, roles and debug output. Strategies use TeamContext / PlayerHandle to issue commands. |
| 4d | Skills | TeamController → SkillExecutor, JointSkillExecutor | Each SkillCommand becomes a running executable skill (GoToPos, HandleBall, Receive, …) that emits a PlayerControlInput per tick. Two-robot passes go through the pass coordinator. |
| 4e | Rules | comply in team_controller.rs | Enforces game-state rules on every robot's input (stop distances, defense-area keep-outs, ball placement). Yellow-carded robots are benched earlier, by plan_sidelining, before the strategy runs. |
| 4f | Motion | avoidance planner, ORCA, PlayerController, path follower, yaw control | Plans a path around obstacles, follows it, deflects through ORCA, applies acceleration/velocity limits and dribbler/kicker logic. Produces a PlayerCmd and transforms it back to absolute coordinates. |
| 4g | Log | dies-logger worker | Records the frame (world, debug values, raw feedback) to the columnar log. |
| 5 | Send | Executor::player_commands → BasestationHandle::send_no_wait / Simulation::push_cmd | Commands go out over radio, or into the simulator for its next step. |
Side channels
- Web UI control. The React app reads world/debug state over a WebSocket and sends commands via REST (
api.tsx). Backend:server.rs/routes.rs→UiCommand→executor_task.rs→ExecutorHandle/ControlMsg→Executor::handle_control_msg. Rust types reach TypeScript throughtypeshare(generated intowebui/src/bindings.tsbybuild.rs). - Settings and tunables.
ExecutorSettings(persisted indies-settings.json) configures tracker, controller and avoidance. Skill knobs use thetunables!macro (tunable.rs); strategy-declared parameters useparams.rs. All are editable live from the UI. - Debug output. Executor code draws/records with the global functions in
debug_info.rs; strategies usedies-strategy-api/src/debug.rs, which travels over IPC. Both end up in the UI and the log. - Logging and replay. Schema in
schema.rs; logs land inlogs/.replay.rs+replay_controller.rsplay a log back through the live UI channels. Offline analysis:tools/dieslog.pyandtools/match_analytics.py. - Strategy lifecycle. The CLI builds/watches strategy crates (
dies-cli/src/strategy.rs);StrategyHostspawns one process per team and hot-swaps it when the binary's mtime changes. - Headless self-play.
commands/self_play.rs→Executor::run_headless(headless.rs): a deterministic, faster-than-realtime sim match with no UI. - Entry point.
dies-cliparses flags (cli.rs) and starts the UI server, a self-play match, or a hardware test.
Where do I change…?
| I want to… | Start here |
|---|---|
| Change team behaviour: who goes where, when to shoot or pass | strategies/concerto: planner.rs (where the ball should go), driver.rs (turns a plan into skills), formation.rs (everyone else), keeper.rs, penalty.rs, constants in config.rs |
| Change how a robot does one thing (pick up the ball, aim, kick, receive) | dies-executor/src/skills/executable/, e.g. handle_ball/ |
| Add a new skill | Variant in SkillCommand → builder in PlayerHandle / skill_builders.rs → implementation in skills/executable/ → wire it up in create_skill_from_command (skill_executor.rs) |
| Change passing between two robots | pass_coordinator.rs, joint_skill_executor.rs, passing.rs |
| Expose new world information to strategies | WorldData → create_world_snapshot → WorldSnapshot → World accessor |
| Fix noisy or laggy robot/ball estimates | dies-world/src/player.rs, ball.rs, filter/, tuning in TrackerSettings |
| Change who is considered to have the ball | dies-world/src/possession.rs (types in dies-core/src/possession.rs) |
| Change how referee commands map to game states | dies-world/src/game_state.rs, GameState |
| Change rule enforcement (stop distance, defense area, yellow cards) | comply and plan_sidelining in team_controller.rs |
| Change obstacle avoidance or path planning | control/avoidance/ (planner.rs, orca.rs, obstacle.rs), config in avoidance_config.rs |
| Change trajectory tracking, acceleration limits, dribbler ramp | player_controller.rs, path_follower.rs, yaw_control.rs, motion_bounds.rs |
| Change what is sent to the robots / the radio protocol | PlayerCmd, basestation_client.rs |
| Change simulator physics or auto-referee | dies-simulator/src/lib.rs |
| Test a skill in isolation | strategies/scenarios (one binary per scenario) |
| Add a web UI panel or view | webui/src/components/panels/, webui/src/views/, data hooks in api.tsx |
| Add a UI → executor command | UiCommand → executor_task.rs → ControlMsg → Executor::handle_control_msg (lib.rs) |
| Add a REST endpoint | server.rs + routes.rs |
| Add a runtime-tunable knob | tunables! in the skill; generic settings in executor_settings.rs |
| Change what gets logged | schema.rs, builders.rs, worker.rs; reader in tools/dieslog.py |
| Add a CLI flag or subcommand | cli.rs, commands/ |
| Change in-match announcements | announcer.rs |
Ground rules
- Coordinates. Strategies and controllers work in team-relative coordinates (+x toward the opponent goal). The tracker, simulator, UI and log use absolute vision coordinates. Conversion happens only at the boundaries listed above. Never expose absolute coordinates to a strategy.
- Time. Anything in the per-frame path (skills, controllers, FSMs) uses world time (
ctx.world.dt,t_received), neverInstant. The headless sim runs faster than realtime, so a wall-clock timer fires at the wrong moment and breaks determinism. - Dependencies.
dies-coreholds shared types and nothing else. Feature crates depend on it, anddies-cliglues them together. Strategy crates only seedies-strategy-api.