mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-06-29 00:45:55 +08:00
docs: 网络对战功能设计文档
This commit is contained in:
@@ -0,0 +1,159 @@
|
|||||||
|
# Gobang 网络对战设计文档
|
||||||
|
|
||||||
|
> 状态: 已确认 | 日期: 2026-05-31
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
为 Gobang v2.0 实现基于 renet 的 P2P 网络对战,两人通过 IP:端口直连对弈。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ React 前端 ───────────────────────────────────────────┐
|
||||||
|
│ OnlineSetup GameView │
|
||||||
|
│ 创建房间/加入房间 连接状态指示 │
|
||||||
|
│ listen("remote-move") │
|
||||||
|
└─────────────────────┬───────────────────────────────────┘
|
||||||
|
│ invoke / listen
|
||||||
|
┌─ Tauri IPC ─────────┼───────────────────────────────────┐
|
||||||
|
│ commands.rs │ │
|
||||||
|
│ host_game(port) │ 对手落子 → │
|
||||||
|
│ join_game(ip,port) │ app.emit("remote-move") │
|
||||||
|
│ send_move(x,y) │ │
|
||||||
|
│ send_undo() │ AppState.network_tx │
|
||||||
|
│ send_resign() │ channel 发送端 │
|
||||||
|
└─────────┬────────────┴──────────────────────────────────┘
|
||||||
|
│ mpsc::channel (NetworkCmd / NetworkEvent)
|
||||||
|
┌─ 网络线程 ──────────────────────────────────────────────┐
|
||||||
|
│ NetworkLoop │
|
||||||
|
│ Server: RenetServer + NetcodeServerTransport │
|
||||||
|
│ Client: RenetClient + NetcodeClientTransport │
|
||||||
|
│ │
|
||||||
|
│ loop { update → recv → process → send_packets } │
|
||||||
|
│ 16ms 帧率,消息通过 channel 与 commands 层通信 │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 通信模型
|
||||||
|
|
||||||
|
renet 内置 Server/Client。主机运行 RenetServer,对手作为 RenetClient 连接。所有消息经主机转发。
|
||||||
|
|
||||||
|
### 消息协议
|
||||||
|
|
||||||
|
网络传输使用 serde JSON + renet ReliableOrdered 通道:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
enum NetMessage {
|
||||||
|
Move { x: usize, y: usize, turn: u32 },
|
||||||
|
Undo { steps: u32 },
|
||||||
|
Resign,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Channel 接口
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// commands → 网络线程
|
||||||
|
enum NetworkCmd {
|
||||||
|
SendMove { x: usize, y: usize, turn: u32 },
|
||||||
|
SendUndo { steps: u32 },
|
||||||
|
SendResign,
|
||||||
|
Shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络线程 → commands
|
||||||
|
enum NetworkEvent {
|
||||||
|
RemoteMove { x: usize, y: usize },
|
||||||
|
RemoteUndo { steps: u32 },
|
||||||
|
RemoteResign,
|
||||||
|
ClientConnected,
|
||||||
|
ClientDisconnected,
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 连接流程
|
||||||
|
|
||||||
|
```
|
||||||
|
主机 (Server) 对手 (Client)
|
||||||
|
host_game(port)
|
||||||
|
绑定 UDP "0.0.0.0:{port}"
|
||||||
|
NetworkLoop::Server 启动
|
||||||
|
返回实际端口
|
||||||
|
emit("waiting") ───────────────────→ 对手 join_game(ip, port)
|
||||||
|
bind UDP "0.0.0.0:0"
|
||||||
|
NetworkLoop::Client 启动
|
||||||
|
connect to server ──→
|
||||||
|
← connected ─────────
|
||||||
|
收到 ClientConnected
|
||||||
|
emit("opponent-joined") ←──────────→ emit("connected")
|
||||||
|
|
||||||
|
游戏开始,黑方(主机)先手正常落子
|
||||||
|
place_piece → NetworkCmd::SendMove ─→ broadcast ─→ client recv
|
||||||
|
NetworkEvent::RemoteMove
|
||||||
|
invoke place_piece
|
||||||
|
```
|
||||||
|
|
||||||
|
## 生命周期
|
||||||
|
|
||||||
|
- 网络线程在 `new_game(Online)` 时 spawn
|
||||||
|
- 游戏结束或 AppState drop 时:发送 Shutdown → 线程退出 → join
|
||||||
|
- 对手断开:主机的 ClientDisconnected event → emit 对手获胜
|
||||||
|
|
||||||
|
## 前端改动
|
||||||
|
|
||||||
|
### OnlineSetup
|
||||||
|
- 创建房间:输入端口号(可选,默认随机),显示"我的地址: IP:端口"
|
||||||
|
- 加入房间:输入"IP:端口",连接
|
||||||
|
|
||||||
|
### GameView — 新增连接状态条
|
||||||
|
- 等待中:显示"等待对手加入... (你的地址: IP:端口)"
|
||||||
|
- 已连接:显示"已连接"
|
||||||
|
- 已断开:显示"对手断开连接" + 对手获胜
|
||||||
|
|
||||||
|
### BoardCanvas — 监听 remote-move
|
||||||
|
```typescript
|
||||||
|
useEffect(() => {
|
||||||
|
const unlisten = listen<{ x: number; y: number }>('remote-move', (event) => {
|
||||||
|
placePiece(event.payload.x, event.payload.y);
|
||||||
|
});
|
||||||
|
return () => { unlisten.then(fn => fn()); };
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
### GameControls
|
||||||
|
- Online 模式:不显示悔棋(需双方同意,暂不做)
|
||||||
|
- 认输:调用 send_resign
|
||||||
|
|
||||||
|
## AppState 改动
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct AppState {
|
||||||
|
// ... 现有字段 ...
|
||||||
|
pub network_tx: Mutex<Option<Sender<NetworkCmd>>>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# core/Cargo.toml & gui/Cargo.toml
|
||||||
|
renet = { version = "0.0.23", features = ["netcode"] }
|
||||||
|
renet_netcode = "0.0.15"
|
||||||
|
serde_json = "1" # 已有
|
||||||
|
```
|
||||||
|
|
||||||
|
## 不做 (YAGNI)
|
||||||
|
|
||||||
|
- Chat 功能(NetMessage 保留类型但无 UI)
|
||||||
|
- NAT 穿透 / 中转服务器
|
||||||
|
- 断线重连
|
||||||
|
- 观战模式
|
||||||
|
- 悔棋双方确认(Online 模式直接禁悔棋)
|
||||||
|
|
||||||
|
## 测试策略
|
||||||
|
|
||||||
|
- Rust 单元测试:NetMessage serde 往返、NetworkCmd/Event channel 通信、NetworkLoop::new 创建
|
||||||
|
- 集成测试:renet 本地 client 模拟(server.new_local_client)
|
||||||
|
- 前端测试:OnlineSetup 组件渲染、状态文本切换
|
||||||
|
- 手动测试:双窗口(host + join localhost)
|
||||||
Reference in New Issue
Block a user