update CLAUDE.md, README.md and justfile
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
- always check with `cargo clippy` and fix the issues
|
||||
- always do a final `cargo +nightly fmt` after you're done with all changes
|
||||
- use `cargo tauri build --debug --no-bundle` if you need to build the frontend (a.k.a. `lanspread-tauri-deno-ts` crate)
|
||||
@@ -1,191 +1,32 @@
|
||||
# CLAUDE.md
|
||||
# lanspread
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
Peer-to-peer game library sharing for LAN parties. Peers discover each other on the local network via mDNS, exchange library metadata over QUIC, and let users browse and download games from each other. Ships as a Tauri desktop app.
|
||||
|
||||
## Project Overview
|
||||
## Workspace layout
|
||||
|
||||
**LanSpread2** is a peer-to-peer (P2P) game distribution system with a Tauri desktop application frontend. It enables users to share games over local networks using mDNS discovery and QUIC protocol for peer communication.
|
||||
Cargo workspace under `crates/`:
|
||||
|
||||
## Common Development Commands
|
||||
- `lanspread-peer` — core peer: networking, library, downloads, services. Start here for behavior changes. See `crates/lanspread-peer/ARCHITECTURE.md`.
|
||||
- `lanspread-proto` — wire protocol types shared across peers.
|
||||
- `lanspread-mdns` — mDNS-SD discovery wrapper.
|
||||
- `lanspread-db` — database/schema types (sqlx + sqlite).
|
||||
- `lanspread-compat` — compatibility/migration glue between db and other crates.
|
||||
- `lanspread-utils` — small shared helpers.
|
||||
- `lanspread-tauri-deno-ts/` — frontend (Vite + Deno + TS in `src/`) and Tauri shell (`src-tauri/`). This is the GUI client.
|
||||
|
||||
### Build
|
||||
Top-level `Cargo.toml` pins workspace dependency versions; per-crate `Cargo.toml`s set lints (pedantic clippy, `unsafe_code = forbid` on most).
|
||||
|
||||
```bash
|
||||
# Development (opens UI, needs user interaction)
|
||||
cargo tauri dev
|
||||
## Commands (justfile)
|
||||
|
||||
# Debug build for very quick testing (without bundle)
|
||||
cargo tauri build --debug --no-bundle
|
||||
Never use normal cargo ... commands, use the just ... commands instead.
|
||||
|
||||
# Release build for testing (without bundling)
|
||||
cargo tauri build --no-bundle
|
||||
- `just run` — run the GUI in dev mode.
|
||||
- `just build` — build the GUI without bundling (also dev mode).
|
||||
- `just fmt` — format the workspace.
|
||||
- `just clippy` — lint the workspace.
|
||||
- `just fix` — auto-apply cargo/clippy fixes, then format.
|
||||
- `just clean` — wipe the build cache.
|
||||
|
||||
# Production (with bundling)
|
||||
cargo tauri build -- --profile release-lto
|
||||
```
|
||||
## General info
|
||||
|
||||
### Build Profiles
|
||||
|
||||
- `release`: Default release profile with debug info, assertions enabled, overflow checks on, for testing
|
||||
- `release-lto`: Optimized for distribution with LTO enabled, debug symbols stripped, smaller binary, only for production
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# Lint all code
|
||||
cargo clippy
|
||||
|
||||
# Format code (nightly Rust)
|
||||
cargo +nightly fmt
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
cargo test --workspace
|
||||
|
||||
# Run tests for a specific crate
|
||||
cargo test -p lanspread-peer
|
||||
|
||||
# Run a specific test
|
||||
cargo test -p lanspread-peer test_name
|
||||
|
||||
# Run tests with output
|
||||
cargo test -- --nocapture
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
```bash
|
||||
# Update frontend dependencies (Deno)
|
||||
deno outdated --update --latest
|
||||
```
|
||||
|
||||
### Wayland Users
|
||||
|
||||
```bash
|
||||
# Set this environment variable if running on Wayland
|
||||
WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
LanSpread follows a layered, modular architecture with clear separation of concerns:
|
||||
|
||||
### Crate Structure
|
||||
|
||||
The project is organized as a Cargo workspace with 7 crates:
|
||||
|
||||
**Foundation Layer:**
|
||||
|
||||
- **lanspread-utils**: Utility macros and helpers used across all crates
|
||||
- **lanspread-db**: Core data structures (`Game`, `GameFileDescription`, game metadata)
|
||||
|
||||
**Protocol & Communication:**
|
||||
|
||||
- **lanspread-proto**: P2P communication protocol definitions
|
||||
- Message types: `Request` (Ping, ListGames, GetGame, etc.), `Response`, `Message` trait
|
||||
- Serialization: JSON with `serde`
|
||||
|
||||
**Network Discovery & Compatibility:**
|
||||
|
||||
- **lanspread-mdns**: mDNS service discovery and advertisement
|
||||
- Advertises and discovers "_lanspread._udp.local." services on LAN
|
||||
- Uses `mdns-sd` crate
|
||||
|
||||
- **lanspread-compat**: Compatibility layer for legacy ETI game database format
|
||||
- Reads legacy `game.db` SQLite databases
|
||||
- Converts `EtiGame` structs to modern `Game` struct
|
||||
|
||||
**Core P2P Engine:**
|
||||
|
||||
- **lanspread-peer**: Central orchestration for all P2P functionality
|
||||
- Entry point: `start_peer()` in `lib.rs`
|
||||
- Communicates via unbounded channels (`PeerEvent`, `PeerCommand`)
|
||||
- Key modules:
|
||||
- `services.rs`: Background tasks (server, discovery, ping, local game monitor)
|
||||
- `handlers.rs`: Command handlers for UI requests
|
||||
- `network.rs`: QUIC connection management with length-delimited frames
|
||||
- `download.rs`: Chunked file transfer logic
|
||||
- `peer_db.rs`: Peer tracking and game availability
|
||||
- `path_validation.rs`: Security for file operations
|
||||
|
||||
**Application Layer:**
|
||||
|
||||
- **lanspread-tauri-deno-ts**: Tauri desktop application
|
||||
- Binary + Library crate for the UI layer
|
||||
- IPC commands: `request_games`, `install_game`, etc.
|
||||
- Manages `LanSpreadState` for peer controller, game DB, and downloads
|
||||
- Uses Tauri plugins: logging, shell, dialogs, persistent storage
|
||||
- Allocates MiMalloc for memory efficiency
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
User (Tauri UI)
|
||||
↓ IPC Commands (request_games, install_game)
|
||||
↓
|
||||
lanspread-tauri-deno-ts (app layer)
|
||||
↓
|
||||
lanspread-peer (P2P engine)
|
||||
├─ lanspread-mdns (discovers peers via mDNS)
|
||||
├─ lanspread-proto (constructs P2P messages)
|
||||
├─ lanspread-compat (reads legacy game databases)
|
||||
└─ QUIC Network ↔ Other Peers
|
||||
↓
|
||||
lanspread-db (game data structures)
|
||||
↓
|
||||
Result → Back to UI
|
||||
```
|
||||
|
||||
### Key Architectural Details
|
||||
|
||||
1. **Async/Await**: Tokio runtime throughout for non-blocking I/O
|
||||
2. **Message Passing**: Unbounded channels for inter-component communication (events and commands)
|
||||
3. **Protocol**: Length-delimited QUIC frames with JSON serialization
|
||||
4. **Service Discovery**: Automatic mDNS announcement and discovery of peers
|
||||
5. **File Streaming**: Chunked transfer with path validation for security
|
||||
6. **Legacy Support**: Backwards compatibility with ETI game database format
|
||||
7. **Error Handling**: Custom error types via `eyre` crate
|
||||
|
||||
## Development Practices
|
||||
|
||||
### From AGENTS.md
|
||||
|
||||
- Always check code with `cargo clippy` and fix any issues
|
||||
- Always format with `cargo +nightly fmt` after completing changes
|
||||
- Use appropriate Rust log levels when debugging:
|
||||
- `RUST_LOG=lanspread=debug` for module-specific debugging
|
||||
- `RUST_LOG=info,lanspread=debug` for general info + module debug
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- Use `release-lto` profile for final builds to enable Link-Time Optimization
|
||||
- MiMalloc is used for the Tauri app to reduce memory overhead
|
||||
- QUIC protocol chosen for efficient P2P communication
|
||||
- Length-delimited framing reduces message parsing overhead
|
||||
|
||||
## Important Implementation Notes
|
||||
|
||||
### Logging
|
||||
|
||||
The codebase uses `tracing` and `log` crates. Note: There's documented uncertainty about their exact relationship (see LESSONS_LEARNED.md). The client app uses `log` crate; be consistent with whichever is used in the component you're modifying.
|
||||
|
||||
### Legacy Database Support
|
||||
|
||||
Some users may have old ETI format game databases. The `lanspread-compat` crate handles reading these. When querying games, both modern and legacy formats are considered.
|
||||
|
||||
### Path Validation
|
||||
|
||||
File transfers validate paths to prevent directory traversal attacks. All file operations use validated paths from `lanspread-peer`.
|
||||
|
||||
### Retry Logic
|
||||
|
||||
`lanspread-peer` includes retry logic for requesting games from peers. This handles transient network failures in P2P discovery.
|
||||
|
||||
## Known Design Decisions
|
||||
|
||||
**Why not Tauri + Leptos?** Leptos adds unnecessary complexity. Tauri is designed to transfer backend Rust to frontend JavaScript world, but with Leptos the frontend becomes Rust, creating a double translation. The current Tauri + Deno/TypeScript approach is cleaner.
|
||||
|
||||
**Why Tauri?** Simple setup, easy development with `cargo tauri dev`, easy testing and bundling with installers, small final binary (~13MB).
|
||||
See `README.md`.
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
## Description
|
||||
|
||||
Simple server and GUI for LAN parties.
|
||||
Peer-to-peer game library sharing for LAN parties.
|
||||
|
||||
- Peers let users browse and download games from each other
|
||||
- they discover each other on the local network via mDNS
|
||||
- they exchange library metadata over QUIC
|
||||
|
||||
Ships as a Tauri desktop app.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -15,33 +21,12 @@ cargo install tauri-cli
|
||||
# install Deno with a package manager or from https://deno.land/
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
#### Frontend
|
||||
### Build or Run
|
||||
|
||||
```bash
|
||||
# Development
|
||||
cargo tauri dev # prefix with RUST_LOG=your_module=debug or similary for more verbose output
|
||||
# build
|
||||
just build
|
||||
|
||||
# Production but for testing and without bundling
|
||||
cargo tauri build --no-bundle
|
||||
|
||||
# Production
|
||||
cargo tauri build -- --profile release-lto # also bundles everything into a nice platform-specific installer
|
||||
|
||||
# on wayland you probably need to set this env var
|
||||
WEBKIT_DISABLE_DMABUF_RENDERER=1
|
||||
|
||||
# update frontend dependencies
|
||||
deno outdated --update --latest
|
||||
```
|
||||
|
||||
#### Backend
|
||||
|
||||
```bash
|
||||
# Development
|
||||
./server.sh [options...] # prefix with RUST_LOG=your_module=debug or similary for more verbose output
|
||||
|
||||
# Production
|
||||
cargo build --profile release-lto -p lanspread-server
|
||||
# run
|
||||
just run
|
||||
```
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
- `FIRST_START_DONE_FILE: &str = ".softlan_first_start_done"` has to be ignored in game files
|
||||
- folder within folder with game_id has to be ignored in game files (that's the actual installed game, right?)
|
||||
@@ -5,6 +5,7 @@ the current idea: mDNS discovery, QUIC transport, on-demand metadata, and
|
||||
chunked file transfers.
|
||||
|
||||
## Goals (unchanged)
|
||||
|
||||
- Local LAN discovery via mDNS.
|
||||
- QUIC + JSON messages for control, raw streams for file data.
|
||||
- UI drives operations through `PeerCommand`, peers remain headless.
|
||||
@@ -13,6 +14,7 @@ chunked file transfers.
|
||||
## Peer lifecycle and message flow
|
||||
|
||||
### 1) Startup and advertise
|
||||
|
||||
- Start QUIC server.
|
||||
- Advertise via mDNS with TXT records:
|
||||
- `peer_id` (stable ID, not tied to IP)
|
||||
@@ -21,7 +23,9 @@ chunked file transfers.
|
||||
- optional `hostname`
|
||||
|
||||
### 2) Discovery and handshake
|
||||
|
||||
When a peer is discovered:
|
||||
|
||||
1. Connect and send `Hello { peer_id, proto_ver, library_rev, library_digest, features }`.
|
||||
2. Receive `HelloAck { peer_id, proto_ver, library_rev, library_digest, features }`.
|
||||
3. If the remote `peer_id` is already known but the address changed, update it.
|
||||
@@ -32,11 +36,13 @@ When a peer is discovered:
|
||||
- Otherwise request `LibrarySnapshot`.
|
||||
|
||||
### 3) Steady state
|
||||
|
||||
- Any message updates `last_seen`.
|
||||
- Pings run only when idle (or on a longer interval), not every 5 seconds.
|
||||
- Library updates are pushed as deltas, debounced and coalesced.
|
||||
|
||||
### 4) Shutdown
|
||||
|
||||
- Optional `Goodbye { peer_id }` lets others remove the peer quickly.
|
||||
- If a peer vanishes without goodbye, stale timeout + ping removal handle it.
|
||||
- Goodbye is a hint, never required for correctness.
|
||||
@@ -44,20 +50,24 @@ When a peer is discovered:
|
||||
## Library sync protocol
|
||||
|
||||
### Summary and snapshot
|
||||
|
||||
- `LibrarySummary { library_rev, library_digest, game_count }`
|
||||
- `LibrarySnapshot { library_rev, games: Vec<GameSummary> }`
|
||||
|
||||
### Delta updates
|
||||
|
||||
- `LibraryDelta { from_rev, to_rev, added, updated, removed }`
|
||||
- `removed` is a list of game IDs.
|
||||
- Deltas are idempotent; ignore if `to_rev` <= known rev.
|
||||
|
||||
### GameSummary (concept)
|
||||
|
||||
- `id`, `name`, `eti_version`, `size`, `downloaded`, `installed`
|
||||
- `manifest_hash` (hash of file list + sizes)
|
||||
- `availability` (e.g., `ready`, `downloading`, `local_only`)
|
||||
|
||||
## When peers broadcast their game list
|
||||
|
||||
- Only on changes, not on a timer.
|
||||
- Use a short debounce (1-2 seconds) to coalesce bursts of filesystem events.
|
||||
- Send `LibraryDelta` to known peers; send `LibrarySummary` on new connections.
|
||||
@@ -65,6 +75,7 @@ When a peer is discovered:
|
||||
## Local game scanning: fast and low cost
|
||||
|
||||
### Strategy
|
||||
|
||||
1. Maintain a persistent on-disk index (per game):
|
||||
- `manifest_hash`, total size, file list (optional), and a fingerprint
|
||||
(version.ini mtime, .eti mtime/size, local install dir presence).
|
||||
@@ -73,6 +84,7 @@ When a peer is discovered:
|
||||
missed events.
|
||||
|
||||
### Fast-path scanning
|
||||
|
||||
- On startup, list only top-level game directories.
|
||||
- For each game, read a cheap fingerprint:
|
||||
- `.eti` size + mtime
|
||||
@@ -82,20 +94,24 @@ When a peer is discovered:
|
||||
- Only run a recursive scan for new or changed games.
|
||||
|
||||
### Result
|
||||
|
||||
Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
|
||||
## File manifests and downloads
|
||||
|
||||
- Keep `GetGame`/manifest requests, but keyed by `manifest_hash` so repeated
|
||||
calls can be skipped when unchanged.
|
||||
- Downloads remain chunked QUIC streams with the existing integrity checks.
|
||||
|
||||
## Fault tolerance rules
|
||||
|
||||
- Every peer is keyed by `peer_id`, not by IP address.
|
||||
- `library_rev` is monotonic and guards against out-of-order updates.
|
||||
- Any mismatch or missing delta falls back to `LibrarySnapshot`.
|
||||
- Loss of goodbye is harmless; stale timeout is authoritative.
|
||||
|
||||
## Roadmap from current design to this one
|
||||
|
||||
1. Protocol updates in `lanspread-proto`:
|
||||
- Define `Hello`, `HelloAck`, `LibrarySummary`, `LibrarySnapshot`,
|
||||
`LibraryDelta`, and optional `Goodbye` messages.
|
||||
|
||||
@@ -67,7 +67,7 @@ When the UI asks to download a game:
|
||||
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
|
||||
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,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Tauri + React + Typescript
|
||||
|
||||
This template should help get you started developing with Tauri, React and Typescript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
||||
@@ -1,15 +1,12 @@
|
||||
export RUSTFLAGS := "-C target-cpu=native"
|
||||
export WEBKIT_DISABLE_COMPOSITING_MODE := "1"
|
||||
|
||||
server:
|
||||
cargo build -p lanspread-server
|
||||
|
||||
client:
|
||||
cargo tauri dev
|
||||
|
||||
buildclient:
|
||||
build:
|
||||
cargo tauri build --no-bundle -- --profile dev
|
||||
|
||||
run:
|
||||
cargo tauri dev
|
||||
|
||||
fmt:
|
||||
cargo +nightly fmt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user