mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-06-28 16:35:55 +08:00
feat: 重写 network.rs — NetMessage/NetworkCmd/NetworkEvent + bincode serde 测试
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+115
-39
@@ -1,61 +1,137 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// 游戏网络消息
|
/// 网络传输的游戏消息(bincode 序列化)
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum GameMessage {
|
pub enum NetMessage {
|
||||||
Move { x: usize, y: usize, turn: u32 },
|
Move { x: usize, y: usize, turn: u32 },
|
||||||
Undo { steps: u32 },
|
Undo { steps: u32 },
|
||||||
Resign,
|
Resign,
|
||||||
Chat(String),
|
|
||||||
Heartbeat,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 网络连接角色
|
impl NetMessage {
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
bincode::serialize(self).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(data: &[u8]) -> Option<Self> {
|
||||||
|
bincode::deserialize(data).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// commands 层 → 网络线程
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NetworkCmd {
|
||||||
|
SendMove { x: usize, y: usize, turn: u32 },
|
||||||
|
SendUndo { steps: u32 },
|
||||||
|
SendResign,
|
||||||
|
Shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 网络线程 → commands 层
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum NetworkEvent {
|
||||||
|
RemoteMove { x: usize, y: usize },
|
||||||
|
RemoteUndo { steps: u32 },
|
||||||
|
RemoteResign,
|
||||||
|
ClientConnected,
|
||||||
|
ClientDisconnected,
|
||||||
|
Error(String),
|
||||||
|
Listening(u16),
|
||||||
|
Connected,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 网络角色
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum NetworkRole {
|
pub enum NetworkRole {
|
||||||
Server,
|
Server,
|
||||||
Client,
|
Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 网络会话配置
|
/// 网络循环句柄(在独立线程中运行)
|
||||||
#[derive(Debug, Clone)]
|
pub struct NetworkLoop {
|
||||||
pub struct NetworkConfig {
|
role: NetworkRole,
|
||||||
pub role: NetworkRole,
|
running: bool,
|
||||||
pub bind_port: u16,
|
cmd_rx: std::sync::mpsc::Receiver<NetworkCmd>,
|
||||||
pub remote_addr: String,
|
event_tx: std::sync::mpsc::Sender<NetworkEvent>,
|
||||||
pub remote_port: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 网络会话状态
|
impl NetworkLoop {
|
||||||
#[derive(Debug, Clone)]
|
/// 创建 Server 端
|
||||||
pub struct NetworkSession {
|
pub fn new_server(
|
||||||
pub role: NetworkRole,
|
cmd_rx: std::sync::mpsc::Receiver<NetworkCmd>,
|
||||||
pub is_connected: bool,
|
event_tx: std::sync::mpsc::Sender<NetworkEvent>,
|
||||||
pub config: NetworkConfig,
|
) -> Self {
|
||||||
pending_messages: Vec<GameMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkSession {
|
|
||||||
pub fn new(config: NetworkConfig) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
role: config.role,
|
role: NetworkRole::Server,
|
||||||
is_connected: false,
|
running: false,
|
||||||
config,
|
cmd_rx,
|
||||||
pending_messages: Vec::new(),
|
event_tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 发送消息 (实际 renet 集成在 gui 层处理)
|
/// 创建 Client 端
|
||||||
pub fn enqueue_message(&mut self, msg: GameMessage) {
|
pub fn new_client(
|
||||||
self.pending_messages.push(msg);
|
cmd_rx: std::sync::mpsc::Receiver<NetworkCmd>,
|
||||||
}
|
event_tx: std::sync::mpsc::Sender<NetworkEvent>,
|
||||||
|
) -> Self {
|
||||||
/// 取出待发送的消息
|
Self {
|
||||||
pub fn drain_messages(&mut self) -> Vec<GameMessage> {
|
role: NetworkRole::Client,
|
||||||
std::mem::take(&mut self.pending_messages)
|
running: false,
|
||||||
}
|
cmd_rx,
|
||||||
|
event_tx,
|
||||||
pub fn set_connected(&mut self, connected: bool) {
|
}
|
||||||
self.is_connected = connected;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_net_message_move_roundtrip() {
|
||||||
|
let msg = NetMessage::Move { x: 7, y: 7, turn: 0 };
|
||||||
|
let bytes = msg.to_bytes();
|
||||||
|
let decoded = NetMessage::from_bytes(&bytes).unwrap();
|
||||||
|
match decoded {
|
||||||
|
NetMessage::Move { x, y, turn } => {
|
||||||
|
assert_eq!(x, 7);
|
||||||
|
assert_eq!(y, 7);
|
||||||
|
assert_eq!(turn, 0);
|
||||||
|
}
|
||||||
|
_ => panic!("wrong variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_net_message_undo_roundtrip() {
|
||||||
|
let msg = NetMessage::Undo { steps: 1 };
|
||||||
|
let bytes = msg.to_bytes();
|
||||||
|
let decoded = NetMessage::from_bytes(&bytes).unwrap();
|
||||||
|
assert!(matches!(decoded, NetMessage::Undo { steps: 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_net_message_resign_roundtrip() {
|
||||||
|
let msg = NetMessage::Resign;
|
||||||
|
let bytes = msg.to_bytes();
|
||||||
|
let decoded = NetMessage::from_bytes(&bytes).unwrap();
|
||||||
|
assert!(matches!(decoded, NetMessage::Resign));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::Shutdown).unwrap();
|
||||||
|
|
||||||
|
let mut received = Vec::new();
|
||||||
|
while let Ok(cmd) = rx.try_recv() {
|
||||||
|
match cmd {
|
||||||
|
NetworkCmd::Shutdown => break,
|
||||||
|
NetworkCmd::SendMove { x, y, turn } => received.push((x, y, turn)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(received, vec![(7, 7, 0)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user