Files
lanspread/crates/lanspread-peer
2026-02-26 20:12:25 +01:00
..
asd
2026-02-26 20:12:25 +01:00
asd
2026-02-26 20:12:25 +01:00

lanspread-peer

lanspread-peer is the networking runtime that lets Lanspread nodes find each other on the local network, exchange library metadata, and transfer game files. It is designed to run headless other crates (most notably lanspread-tauri-deno-ts) embed it and drive it through a channel-based API.

Runtime Overview

  • start_peer(game_dir, tx_events, peer_game_db) boots the asynchronous runtime in the background and returns an UnboundedSender<PeerCommand> that the caller uses for control. The function immediately forwards the supplied game directory via PeerCommand::SetGameDir and keeps using the provided PeerGameDB so the UI layer can observe live peer metadata.
  • PeerCommand represents the small control surface exposed to the UI layer: ListGames, GetGame, DownloadGameFiles, and SetGameDir.
  • PeerEvent enumerates everything the peer runtime reports back to the UI: library snapshots, download lifecycle updates, and peer membership changes.
  • PeerGameDB collects remote peer metadata. It aggregates discovered peers Game definitions, tracks the latest ETI version per title, and keeps the last seen list of GameFileDescription entries for each peer.

Internally the peer runtime owns three long-lived tasks that run for the lifetime of the process:

  1. Server component (run_server_component) listens for QUIC connections, advertises via mDNS, and serves Request::ListGames, Request::GetGame, Request::GetGameFileData, and Request::GetGameFileChunk by reading from the local game directory.
  2. Discovery loop (run_peer_discovery) uses the lanspread-mdns helper to discover other peers. The blocking mDNS work is executed on a dedicated thread via tokio::task::spawn_blocking so that the Tokio runtime remains responsive.
  3. Ping service (run_ping_service) periodically issues QUIC ping requests to keep peer liveness up to date and prunes stale entries from PeerGameDB.

scan_local_library maintains a lightweight on-disk index and produces both a GameDB and protocol summaries. The resulting database is used to respond to incoming metadata requests (Request::ListGames / Request::GetGame).

Networking and File Transfer

  • Transport is handled by s2n-quic; TLS cert/key material is compiled in from the repository root.
  • Protocol messages are JSON-encoded structures defined in lanspread-proto::{Request, Response}.
  • File transfers stream raw bytes over dedicated bidirectional QUIC streams. peer::send_game_file_data sends entire files, while peer::send_game_file_chunk services ranged requests.

Download Pipeline

When the UI asks to download a game:

  1. The UI first issues PeerCommand::GetGame. Each peer that still reports the game is queried via request_game_details_from_peer, and their file manifests are merged inside PeerGameDB.
  2. Once the UI receives PeerEvent::GotGameFiles, it forwards the selected file list back with PeerCommand::DownloadGameFiles.
  3. download_game_files prepares the filesystem (creating directories and pre-sizing files where possible), emits PeerEvent::DownloadGameFilesBegin, and builds a per-peer plan (build_peer_plans) that round-robins file chunks across the available peers that advertise the latest version.
  4. Each plan is executed in its own task (download_from_peer). Chunk requests use per-chunk QUIC streams and write into pre-created files. The chunk writer keeps existing data intact and only truncates when we intentionally fall back to a full file transfer, which prevents corruption when multiple peers fill different regions of the same file.
  5. Failures are accumulated and retried (up to MAX_RETRY_COUNT) via retry_failed_chunks. If everything succeeds, PeerEvent::DownloadGameFilesFinished is emitted; otherwise the UI receives PeerEvent::DownloadGameFilesFailed.

Integration with lanspread-tauri-deno-ts

The Tauri application embeds this crate in crates/lanspread-tauri-deno-ts/src-tauri/src/lib.rs:

  • LanSpreadState holds onto the peer control channel, the latest aggregated GameDB, per-game download state, and the user-selected game directory.
  • The Tauri commands (request_games, install_game, update_game, and update_game_directory) translate UI actions into PeerCommands. In particular, update_game_directory records the filesystem path, kicks off the peer runtime on first use, and mirrors the installed/uninstalled state into the UI-facing database.
  • A background task consumes PeerEvents and fans them out to the front-end via Tauri publish/subscribe events (games-list-updated, game-download-*, peer-*). Successful downloads trigger an unrar sidecar to unpack ETI archives and clean up the temporary backup folders that are created when updates begin.
  • When downloads fail the Tauri layer restores the on-disk backup, keeping the previous installation consistent even after partial transfers.

Security & Operational Notes

  • All QUIC connections are TLS encrypted; the shipped certificates are suitable for local-network trust but should be rotated for production deployments.
  • Peer discovery is restricted to the local link via mDNS.
  • Long-running blocking mDNS calls are isolated on dedicated threads which keeps the async runtime responsive even when discovery takes a long time.
  • File writes are chunk-safe: partial chunk downloads now open files without truncating existing data, avoiding the corruption that occurred previously when multiple peers collectively filled a file.

Known Limitations

  • PeerGameDB currently models the latest metadata that other peers advertise. If the UI needs to surface titles that only exist locally, additional merging with the locally scanned GameDB will be required.
  • The download planner uses a simple round-robin and does not yet take per-peer throughput or failures into account when distributing work.

Refer to the source (particularly src/lib.rs) for the exact message shapes and state machines.