运行模式

RustyWarfare 支持三种运行模式,所有模式使用相同的游戏规则,只是 server 和 client 的部署位置不同。

模式概览

模式Server 位置Client 位置通信方式用途
单人模式本地本地内存通道单人游戏
主机模式本地本地+远程本地内存+UDP局域网/在线主机
远程模式远程本地UDP加入他人房间
专用服务器独立进程远程UDP持久化服务器

单人模式 (Singleplayer)

架构

┌──────────────────────────────────┐
│     Godot + gdextension          │
│  ┌────────────────────────────┐  │
│  │     runtime_core           │  │
│  │  ┌──────────┐  ┌─────────┐ │  │
│  │  │  Client  │←→│ Server  │ │  │
│  │  └──────────┘  └─────────┘ │  │
│  └────────────────────────────┘  │
└──────────────────────────────────┘
        单一进程

特点

  • 低延迟:无网络开销
  • 离线可玩:不需要网络连接
  • 完整预测:仍然使用预测机制,保持架构一致性
  • 调试友好:所有代码在同一进程

数据流

1. Godot 采集输入
2. gdextension → runtime_core
3. client.process_input()
4. client → server (内存通道,CrossbeamIo)
5. server 执行权威逻辑
6. server → client (内存复制)
7. client.reconcile()
8. runtime_core.get_snapshot()
9. gdextension → Godot

启动代码

#![allow(unused)]
fn main() {
impl Runtime {
    pub fn start_singleplayer(&mut self) -> Result<()> {
        // 加载内容包
        let content_db = self.load_content()?;
        
        // 创建本地 server
        let server = Server::new_local(content_db)?;
        
        // 创建本地 client
        let client = Client::new_local()?;
        
        // 建立内存通道
        let (tx, rx) = crossbeam_channel::unbounded();
        server.connect_channel(rx);
        client.connect_channel(tx);
        
        self.server = Some(server);
        self.client = Some(client);
        self.mode = RuntimeMode::Singleplayer;
        
        Ok(())
    }
}
}

主机模式 (Host)

架构

本机玩家:
┌────────────────────────────────────┐
│     Godot + gdextension            │
│  ┌──────────────────────────────┐  │
│  │     runtime_core             │  │
│  │  ┌──────────┐  ┌───────────┐ │  │
│  │  │  Client  │←→│  Server   │ │  │
│  │  └──────────┘  └─────┬─────┘ │  │
│  └────────────────────────┼──────┘  │
└────────────────────────────┼────────┘
                             │ UDP
                             ↓
                      ┌──────────────┐
                      │ 远程 Client  │
                      └──────────────┘

特点

  • 混合通信:本机用内存通道,远程用 UDP
  • 主机优势:本机玩家零延迟
  • 灵活性:可随时开始/停止接受连接

数据流

本机玩家

同单人模式 (内存通道)

远程玩家

1. 远程 Client 发送输入 → UDP → 本机 Server
2. 本机 Server 执行权威逻辑
3. 本机 Server → UDP → 远程 Client (复制状态)
4. 远程 Client reconcile 并显示

启动代码

#![allow(unused)]
fn main() {
impl Runtime {
    pub fn start_host(&mut self, port: u16) -> Result<()> {
        let content_db = self.load_content()?;
        
        // 创建网络 server (监听端口)
        let server = Server::new_host(content_db, port)?;
        
        // 创建本地 client
        let client = Client::new_local()?;
        
        // 本地连接用内存通道
        let (tx, rx) = crossbeam_channel::unbounded();
        server.add_local_client(rx);
        client.connect_channel(tx);
        
        // Server 同时监听 UDP 端口
        
        self.server = Some(server);
        self.client = Some(client);
        self.mode = RuntimeMode::Host { port };
        
        Ok(())
    }
}
}

远程模式 (Client)

架构

本地:
┌────────────────────────────┐
│   Godot + gdextension      │
│  ┌──────────────────────┐  │
│  │   runtime_core       │  │
│  │  ┌──────────┐        │  │
│  │  │  Client  │        │  │
│  │  └─────┬────┘        │  │
│  └────────┼─────────────┘  │
└────────────┼───────────────┘
             │ UDP
             ↓
      ┌─────────────┐
      │远程 Server  │
      └─────────────┘

特点

  • 轻量级:不运行 server
  • 预测 + 插值:掩盖网络延迟
  • 自动校正:服务器权威,客户端接受校正

数据流

1. Godot 采集输入
2. Client 立即本地预测
3. Client → UDP → Server (发送输入)
4. [网络延迟...]
5. Server 执行权威逻辑
6. Server → UDP → Client (复制状态)
7. Client reconcile (对比预测和权威)
8. Client interpolate (平滑过渡)
9. 显示最终状态

预测与校正

时刻 t=0: 用户点击移动
时刻 t=0: Client 预测 → 单位立即开始移动
时刻 t=0: 发送输入到 Server

时刻 t=50ms: Server 收到输入
时刻 t=50ms: Server 执行权威移动
时刻 t=50ms: Server 返回权威状态

时刻 t=100ms: Client 收到权威状态
时刻 t=100ms: Client 对比预测和权威
时刻 t=100ms: 如有偏差,平滑校正

启动代码

#![allow(unused)]
fn main() {
impl Runtime {
    pub fn start_client(&mut self, server_addr: &str) -> Result<()> {
        // 仅创建 client
        let client = Client::new_remote(server_addr)?;
        
        self.client = Some(client);
        self.server = None;  // 无本地 server
        self.mode = RuntimeMode::Client {
            server_addr: server_addr.to_string(),
        };
        
        Ok(())
    }
}
}

专用服务器 (Dedicated Server)

架构

专用服务器 (独立进程):
┌────────────────────┐
│  runtime_core      │
│  ┌──────────────┐  │
│  │   Server     │  │
│  └──────┬───────┘  │
└─────────┼──────────┘
          │ UDP
    ┌─────┴─────┐
    ↓           ↓
┌────────┐  ┌────────┐
│Client 1│  │Client 2│
└────────┘  └────────┘

特点

  • 持久化:不依赖玩家在线
  • 无 GUI:无 Godot,纯 Rust
  • 高性能:无渲染开销
  • 独立部署:可部署到云服务器

组成模块

dedicated_server
    └── runtime_core
        └── server
        └── protocol
        └── content

不包含

  • client
  • gdextension
  • ❌ Godot

启动代码

// 独立的 dedicated_server 二进制
fn main() {
    let mut runtime = Runtime::new();
    
    runtime.start_dedicated_server(
        port: 5000,
        max_players: 8,
    )?;
    
    loop {
        runtime.update(TICK_DURATION)?;
        thread::sleep(TICK_DURATION);
    }
}

模式对比

包含的模块

模块单人主机远程专用
gdextension
runtime_core
client
server
protocol
content

延迟特性

模式本地延迟网络延迟预测插值
单人<1ms有(架构一致)
主机<1ms20-100ms
远程-20-100ms
专用-20-100ms--

切换模式

在运行时不支持模式切换。必须:

  1. 调用 runtime.shutdown()
  2. 调用新模式的 start_*() 方法
#![allow(unused)]
fn main() {
// 从单人模式切换到主机模式
runtime.shutdown()?;
runtime.start_host(5000)?;
}

下一步

了解详细的数据流动:数据流