From 33c1f8d8d40d317908d12dd8bc6457fffc82dbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sun, 31 May 2026 15:10:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20NetworkLoop::run?= =?UTF-8?q?=20=E2=80=94=20Server/Client=20renet=20=E4=B8=BB=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 renet2_netcode 依赖,使用 renet2 + renet2_netcode + renetcode2 三 crate 架构实现完整网络循环。Server 端监听 UDP 端口并通过 channel 广播游戏消息,Client 端连接并双工通信。 Co-Authored-By: Claude Opus 4.7 --- core/Cargo.toml | 1 + core/src/network.rs | 241 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 240 insertions(+), 2 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 425cb24..c81cc6e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -11,4 +11,5 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" reqwest = { version = "0.12", features = ["json", "blocking"] } renet2 = "0.15" +renet2_netcode = "0.15" bincode = "1" diff --git a/core/src/network.rs b/core/src/network.rs index 93c88b2..f09d19c 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -81,6 +81,234 @@ impl NetworkLoop { event_tx, } } + + /// 启动网络主循环(阻塞,在独立线程中调用) + pub fn run(&mut self, server_addr: &str, protocol_id: u64) -> Result<(), String> { + self.running = true; + match self.role { + NetworkRole::Server => self.run_server(protocol_id), + NetworkRole::Client => self.run_client(server_addr, protocol_id), + } + } + + fn run_server(&mut self, protocol_id: u64) -> Result<(), String> { + use std::net::UdpSocket; + use std::time::{Duration, Instant}; + + let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| format!("Server 绑定失败: {}", e))?; + let local_addr = socket.local_addr().map_err(|e| e.to_string())?; + let local_port = local_addr.port(); + let _ = self.event_tx.send(NetworkEvent::Listening(local_port)); + + let connection_config = + renet2::ConnectionConfig::from_shared_channels(vec![renet2::ChannelConfig { + channel_id: 0, + max_memory_usage_bytes: 5 * 1024 * 1024, + send_type: renet2::SendType::ReliableOrdered { + resend_time: Duration::from_millis(300), + }, + }]); + let mut server = renet2::RenetServer::new(connection_config); + + let current_time = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default(); + let server_config = renet2_netcode::ServerSetupConfig { + current_time, + max_clients: 1, + protocol_id, + socket_addresses: vec![vec![local_addr]], + authentication: renet2_netcode::ServerAuthentication::Unsecure, + }; + let native_socket = renet2_netcode::NativeSocket::new(socket) + .map_err(|e| format!("创建 NativeSocket 失败: {}", e))?; + let mut transport = + renet2_netcode::NetcodeServerTransport::new(server_config, native_socket) + .map_err(|e| format!("创建传输层失败: {}", e))?; + + let tick = Duration::from_millis(16); + + while self.running { + let now = Instant::now(); + + // 处理 commands 层发来的指令 + while let Ok(cmd) = self.cmd_rx.try_recv() { + match cmd { + NetworkCmd::SendMove { x, y, turn } => { + let msg = NetMessage::Move { x, y, turn }; + for cid in server.clients_id() { + server.send_message(cid, 0u8, msg.to_bytes()); + } + } + NetworkCmd::SendUndo { steps } => { + let msg = NetMessage::Undo { steps }; + for cid in server.clients_id() { + server.send_message(cid, 0u8, msg.to_bytes()); + } + } + NetworkCmd::SendResign => { + for cid in server.clients_id() { + server.send_message(cid, 0u8, NetMessage::Resign.to_bytes()); + } + } + NetworkCmd::Shutdown => { + self.running = false; + break; + } + } + } + + server.update(tick); + transport + .update(tick, &mut server) + .map_err(|e| format!("传输层更新失败: {e:?}"))?; + + // 接收客户端消息 + for cid in server.clients_id() { + while let Some(data) = server.receive_message(cid, 0u8) { + if let Some(msg) = NetMessage::from_bytes(&data) { + match msg { + NetMessage::Move { x, y, .. } => { + let _ = self.event_tx.send(NetworkEvent::RemoteMove { x, y }); + } + NetMessage::Undo { steps } => { + let _ = self.event_tx.send(NetworkEvent::RemoteUndo { steps }); + } + NetMessage::Resign => { + let _ = self.event_tx.send(NetworkEvent::RemoteResign); + } + } + } + } + } + + // 处理连接事件 + while let Some(event) = server.get_event() { + match event { + renet2::ServerEvent::ClientConnected { .. } => { + let _ = self.event_tx.send(NetworkEvent::ClientConnected); + } + renet2::ServerEvent::ClientDisconnected { .. } => { + let _ = self.event_tx.send(NetworkEvent::ClientDisconnected); + } + } + } + + transport.send_packets(&mut server); + + let elapsed = now.elapsed(); + if elapsed < tick { + std::thread::sleep(tick - elapsed); + } + } + Ok(()) + } + + fn run_client(&mut self, server_addr: &str, protocol_id: u64) -> Result<(), String> { + use std::net::{SocketAddr, UdpSocket}; + use std::time::{Duration, Instant}; + + let server_addr: SocketAddr = server_addr + .parse() + .map_err(|e| format!("地址解析失败: {}", e))?; + let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| format!("Client 绑定失败: {}", e))?; + + let connection_config = + renet2::ConnectionConfig::from_shared_channels(vec![renet2::ChannelConfig { + channel_id: 0, + max_memory_usage_bytes: 5 * 1024 * 1024, + send_type: renet2::SendType::ReliableOrdered { + resend_time: Duration::from_millis(300), + }, + }]); + let mut client = renet2::RenetClient::new(connection_config, false); + + let current_time = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default(); + let authentication = renet2_netcode::ClientAuthentication::Unsecure { + server_addr, + client_id: current_time.as_millis() as u64, + user_data: None, + protocol_id, + socket_id: 0, + }; + let native_socket = renet2_netcode::NativeSocket::new(socket) + .map_err(|e| format!("创建 NativeSocket 失败: {}", e))?; + let mut transport = renet2_netcode::NetcodeClientTransport::new( + current_time, + authentication, + native_socket, + ) + .map_err(|e| format!("创建传输层失败: {}", e))?; + + let tick = Duration::from_millis(16); + let mut was_connected = false; + + while self.running { + let now = Instant::now(); + + while let Ok(cmd) = self.cmd_rx.try_recv() { + match cmd { + NetworkCmd::SendMove { x, y, turn } => { + let msg = NetMessage::Move { x, y, turn }; + client.send_message(0u8, msg.to_bytes()); + } + NetworkCmd::SendUndo { steps } => { + let msg = NetMessage::Undo { steps }; + client.send_message(0u8, msg.to_bytes()); + } + NetworkCmd::SendResign => { + client.send_message(0u8, NetMessage::Resign.to_bytes()); + } + NetworkCmd::Shutdown => { + self.running = false; + break; + } + } + } + + client.update(tick); + transport + .update(tick, &mut client) + .map_err(|e| format!("传输层更新失败: {e:?}"))?; + + if client.is_connected() && !was_connected { + was_connected = true; + let _ = self.event_tx.send(NetworkEvent::Connected); + } + if !client.is_connected() && was_connected { + was_connected = false; + let _ = self.event_tx.send(NetworkEvent::ClientDisconnected); + } + + while let Some(data) = client.receive_message(0u8) { + if let Some(msg) = NetMessage::from_bytes(&data) { + match msg { + NetMessage::Move { x, y, .. } => { + let _ = self.event_tx.send(NetworkEvent::RemoteMove { x, y }); + } + NetMessage::Undo { steps } => { + let _ = self.event_tx.send(NetworkEvent::RemoteUndo { steps }); + } + NetMessage::Resign => { + let _ = self.event_tx.send(NetworkEvent::RemoteResign); + } + } + } + } + + transport + .send_packets(&mut client) + .map_err(|e| format!("发送数据包失败: {e}"))?; + + let elapsed = now.elapsed(); + if elapsed < tick { + std::thread::sleep(tick - elapsed); + } + } + Ok(()) + } } #[cfg(test)] @@ -89,7 +317,11 @@ mod tests { #[test] fn test_net_message_move_roundtrip() { - let msg = NetMessage::Move { x: 7, y: 7, turn: 0 }; + let msg = NetMessage::Move { + x: 7, + y: 7, + turn: 0, + }; let bytes = msg.to_bytes(); let decoded = NetMessage::from_bytes(&bytes).unwrap(); match decoded { @@ -121,7 +353,12 @@ mod tests { #[test] fn test_network_cmd_channel() { let (tx, rx) = std::sync::mpsc::channel(); - tx.send(NetworkCmd::SendMove { x: 7, y: 7, turn: 0 }).unwrap(); + tx.send(NetworkCmd::SendMove { + x: 7, + y: 7, + turn: 0, + }) + .unwrap(); tx.send(NetworkCmd::Shutdown).unwrap(); let mut received = Vec::new();