diff --git a/docs/DEFERRED.md b/docs/DEFERRED.md index dd30ac4..a309f22 100644 --- a/docs/DEFERRED.md +++ b/docs/DEFERRED.md @@ -62,6 +62,14 @@ - **raycast_all (다중 hit)** — 가장 가까운 hit만 반환. - **BVH 조기 종료 최적화** — 모든 리프 검사 후 최소 t 선택. front-to-back 순회 미구현. +## Phase 8-2 + +- **상태 동기화 (스냅샷)** — 미구현. 서버→클라이언트 월드 상태 전송. +- **보간 / 예측** — 미구현. 클라이언트 측 스무딩. +- **지연 보상** — 미구현. 서버 측 히트 판정 보정. +- **신뢰성 계층** — 미구현. 패킷 재전송, 순서 보장. +- **암호화 / 인증** — 미구현. + ## Phase 8-1 - **자동 내비메시 생성** — Recast 스타일 복셀화 미구현. 수동 정의만. diff --git a/docs/STATUS.md b/docs/STATUS.md index ef17d8f..6ea6f99 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -125,6 +125,13 @@ - voltex_ai: A* pathfinding on triangle graph (center-point path) - voltex_ai: Steering behaviors (seek, flee, arrive, wander, follow_path) +### Phase 8-2: Networking Foundation +- voltex_net: Packet protocol (Connect, Accept, Disconnect, Ping, Pong, UserData) +- voltex_net: Binary serialization/deserialization +- voltex_net: Non-blocking UDP socket wrapper +- voltex_net: NetServer (client management, broadcast) +- voltex_net: NetClient (server connection, handshake) + ## Crate 구조 ``` @@ -136,10 +143,11 @@ crates/ ├── voltex_asset — Handle, AssetStorage, Assets ├── voltex_physics — Collider, ContactPoint, BvhTree, RigidBody, detect_collisions, physics_step, raycast ├── voltex_audio — AudioClip, WAV parser, mixing, WASAPI backend, AudioSystem, MixGroup, spatial -└── voltex_ai — NavMesh, A* pathfinding, steering behaviors +├── voltex_ai — NavMesh, A* pathfinding, steering behaviors +└── voltex_net — UDP packets, NetServer, NetClient ``` -## 테스트: 228개 전부 통과 +## 테스트: 236개 전부 통과 - voltex_asset: 14 - voltex_audio: 35 (audio_clip 2 + wav 5 + mixing 11 + audio_system 2 + spatial 8 + mix_group 7) @@ -148,6 +156,7 @@ crates/ - voltex_physics: 52 (collider 2 + narrow 11 + bvh 5 + collision 7 + rigid_body 3 + integrator 3 + solver 5 + ray 10 + raycast 6) - voltex_platform: 3 - voltex_ai: 15 (navmesh 4 + pathfinding 5 + steering 6) +- voltex_net: 8 (packet 7 + integration 1) - voltex_renderer: 33 (20 + SSGI 3 + RT 3 + bloom 3 + tonemap 4) ## Examples (11개) @@ -164,7 +173,7 @@ crates/ - audio_demo — 사인파 오디오 재생 - deferred_demo — 디퍼드 렌더링 + 다중 포인트 라이트 -## 다음: Phase 8-2 (네트워킹) / 8-3 (스크립팅) / 8-4 (에디터) — Stretch Goal +## 다음: Phase 8-3 (스크립팅) / 8-4 (에디터) — Stretch Goal 스펙 참조: `docs/superpowers/specs/2026-03-24-voltex-engine-design.md` diff --git a/docs/superpowers/plans/2026-03-25-phase8-2-networking.md b/docs/superpowers/plans/2026-03-25-phase8-2-networking.md new file mode 100644 index 0000000..808ac09 --- /dev/null +++ b/docs/superpowers/plans/2026-03-25-phase8-2-networking.md @@ -0,0 +1,43 @@ +# Phase 8-2: Networking Foundation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** UDP 기반 클라이언트-서버 네트워킹 인프라 — 패킷 직렬화, 연결 핸드셰이크, 데이터 송수신 + +**Architecture:** `voltex_net` crate 신규. 바이너리 패킷 프로토콜 + non-blocking UdpSocket 래퍼 + NetServer(클라이언트 관리) + NetClient(서버 연결). std::net만 사용. + +**Tech Stack:** Rust, std::net::UdpSocket + +**Spec:** `docs/superpowers/specs/2026-03-25-phase8-2-networking.md` + +--- + +## Task 1: Crate + Packet 직렬화 + +Create crate, packet.rs with Packet enum and binary serialization. + +- Cargo.toml: no dependencies +- Workspace: add members + workspace.dependencies +- Packet enum: Connect, Accept, Disconnect, Ping, Pong, UserData +- Header: [type u8, payload_len u16 LE, reserved u8] +- to_bytes(), from_bytes() +- 6 tests: roundtrip per variant + invalid bytes error + +Commit: `feat(net): add voltex_net crate with packet serialization` + +## Task 2: NetSocket + NetServer + NetClient + +Create socket.rs, server.rs, client.rs with full networking. + +- NetSocket: bind(non-blocking), send_to, recv_from +- ClientInfo, ServerEvent, NetServer: poll, broadcast, send_to_client +- ClientEvent, NetClient: connect, poll, send, is_connected, disconnect +- Tests: localhost handshake, UserData roundtrip, disconnect + +Commit: `feat(net): add UDP server/client with connection management` + +## Task 3: Documentation + +Update STATUS.md and DEFERRED.md. + +Commit: `docs: add Phase 8-2 networking status and deferred items` diff --git a/docs/superpowers/specs/2026-03-25-phase8-2-networking.md b/docs/superpowers/specs/2026-03-25-phase8-2-networking.md new file mode 100644 index 0000000..e3ac393 --- /dev/null +++ b/docs/superpowers/specs/2026-03-25-phase8-2-networking.md @@ -0,0 +1,174 @@ +# Phase 8-2: Networking Foundation — Design Spec + +## Overview + +`voltex_net` crate를 신규 생성한다. UDP 소켓 래핑, 패킷 직렬화, 클라이언트-서버 연결 관리를 구현한다. + +## Scope + +- 패킷 프로토콜 (Connect, Accept, Disconnect, Ping, Pong, UserData) +- 바이너리 직렬화/역직렬화 +- Non-blocking UDP 소켓 래퍼 +- NetServer (클라이언트 관리, broadcast) +- NetClient (서버 연결, 핸드셰이크) + +## Out of Scope + +- 상태 동기화 (스냅샷) +- 보간 / 예측 +- 지연 보상 +- 신뢰성 계층 (재전송, 순서 보장) +- 암호화 / 인증 + +## Module Structure + +``` +crates/voltex_net/ +├── Cargo.toml +└── src/ + ├── lib.rs + ├── packet.rs — Packet enum, to_bytes, from_bytes + ├── socket.rs — NetSocket (UdpSocket wrapper) + ├── server.rs — NetServer + └── client.rs — NetClient +``` + +## Dependencies + +- 없음 (std::net::UdpSocket만 사용) + +## Types + +### Packet + +```rust +#[derive(Debug, Clone, PartialEq)] +pub enum Packet { + Connect { client_name: String }, + Accept { client_id: u32 }, + Disconnect { client_id: u32 }, + Ping { timestamp: u64 }, + Pong { timestamp: u64 }, + UserData { client_id: u32, data: Vec }, +} +``` + +### 직렬화 포맷 + +``` +[type: u8] [payload_len: u16 LE] [reserved: u8] [payload...] +``` + +Type IDs: Connect=1, Accept=2, Disconnect=3, Ping=4, Pong=5, UserData=6 + +Payload encoding: +- Connect: name_len(u16) + name_bytes +- Accept: client_id(u32) +- Disconnect: client_id(u32) +- Ping/Pong: timestamp(u64) +- UserData: client_id(u32) + data_len(u16) + data_bytes + +```rust +impl Packet { + pub fn to_bytes(&self) -> Vec + pub fn from_bytes(data: &[u8]) -> Result +} +``` + +### NetSocket + +```rust +pub struct NetSocket { + socket: UdpSocket, +} +``` + +- `bind(addr: &str) -> Result` — bind + set_nonblocking(true) +- `send_to(&self, packet: &Packet, addr: SocketAddr) -> Result<(), String>` +- `recv_from(&self) -> Option<(Packet, SocketAddr)>` — None if WouldBlock + +### ClientInfo + +```rust +pub struct ClientInfo { + pub id: u32, + pub addr: SocketAddr, + pub name: String, +} +``` + +### NetServer + +```rust +pub struct NetServer { + socket: NetSocket, + clients: HashMap, + next_id: u32, +} +``` + +- `new(addr: &str) -> Result` +- `poll(&mut self) -> Vec` — process incoming packets +- `broadcast(&self, packet: &Packet)` — send to all clients +- `send_to_client(&self, client_id: u32, packet: &Packet)` +- `clients(&self) -> &HashMap` +- `client_count(&self) -> usize` + +### ServerEvent + +```rust +pub enum ServerEvent { + ClientConnected { client_id: u32, name: String }, + ClientDisconnected { client_id: u32 }, + PacketReceived { client_id: u32, packet: Packet }, +} +``` + +### NetClient + +```rust +pub struct NetClient { + socket: NetSocket, + server_addr: SocketAddr, + client_id: Option, + name: String, +} +``` + +- `new(local_addr: &str, server_addr: &str, name: &str) -> Result` +- `connect(&self)` — send Connect packet +- `poll(&mut self) -> Vec` — process incoming +- `send(&self, packet: &Packet) -> Result<(), String>` +- `is_connected(&self) -> bool` +- `client_id(&self) -> Option` +- `disconnect(&self)` + +### ClientEvent + +```rust +pub enum ClientEvent { + Connected { client_id: u32 }, + Disconnected, + PacketReceived { packet: Packet }, +} +``` + +## Test Plan + +### packet.rs +- Connect 라운드트립 +- Accept 라운드트립 +- Disconnect 라운드트립 +- Ping/Pong 라운드트립 +- UserData 라운드트립 +- 잘못된 바이트 → 에러 + +### socket.rs +- localhost 바인드 +- send + recv 라운드트립 + +### server.rs + client.rs (통합) +- 서버 시작 + 클라이언트 연결 → Accept 수신 +- 다수 클라이언트 연결 +- 클라이언트 Disconnect → 서버에서 제거 +- UserData 송수신