mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-06-28 16:35:55 +08:00
feat: 添加 host_game/join_game/send_move/send_undo/send_resign 网络命令
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+130
-2
@@ -1,11 +1,13 @@
|
|||||||
use gobang_core::ai::search::AlphaBetaAi;
|
use gobang_core::ai::search::AlphaBetaAi;
|
||||||
use gobang_core::ai::AiEngine;
|
use gobang_core::ai::AiEngine;
|
||||||
use gobang_core::llm::LlmAi;
|
|
||||||
use gobang_core::board::Board;
|
use gobang_core::board::Board;
|
||||||
|
use gobang_core::llm::LlmAi;
|
||||||
|
use gobang_core::network::{NetworkCmd, NetworkEvent, NetworkLoop};
|
||||||
use gobang_core::rules;
|
use gobang_core::rules;
|
||||||
use gobang_core::types::*;
|
use gobang_core::types::*;
|
||||||
|
use std::sync::mpsc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::State;
|
use tauri::{Emitter, State};
|
||||||
|
|
||||||
/// 应用全局状态
|
/// 应用全局状态
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
@@ -15,6 +17,7 @@ pub struct AppState {
|
|||||||
pub ai_engine: Mutex<Option<std::sync::Arc<dyn AiEngine + Send + Sync>>>,
|
pub ai_engine: Mutex<Option<std::sync::Arc<dyn AiEngine + Send + Sync>>>,
|
||||||
pub current_color: Mutex<Color>,
|
pub current_color: Mutex<Color>,
|
||||||
pub game_over: Mutex<bool>,
|
pub game_over: Mutex<bool>,
|
||||||
|
pub network_tx: Mutex<Option<mpsc::Sender<NetworkCmd>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppState {
|
impl Default for AppState {
|
||||||
@@ -26,12 +29,20 @@ impl Default for AppState {
|
|||||||
ai_engine: Mutex::new(None),
|
ai_engine: Mutex::new(None),
|
||||||
current_color: Mutex::new(Color::Black),
|
current_color: Mutex::new(Color::Black),
|
||||||
game_over: Mutex::new(true),
|
game_over: Mutex::new(true),
|
||||||
|
network_tx: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn new_game(mode: GameMode, config: GameConfig, state: State<AppState>) -> Result<(), String> {
|
pub fn new_game(mode: GameMode, config: GameConfig, state: State<AppState>) -> Result<(), String> {
|
||||||
|
// 清理旧的网络连接
|
||||||
|
if let Ok(mut tx) = state.network_tx.lock() {
|
||||||
|
if let Some(tx) = tx.take() {
|
||||||
|
let _ = tx.send(NetworkCmd::Shutdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let is_vs_ai = mode == GameMode::VsAi;
|
let is_vs_ai = mode == GameMode::VsAi;
|
||||||
let board = Board::new(config.board_size);
|
let board = Board::new(config.board_size);
|
||||||
log::info!("新游戏: mode={:?}, board_size={}", mode, config.board_size);
|
log::info!("新游戏: mode={:?}, board_size={}", mode, config.board_size);
|
||||||
@@ -203,3 +214,120 @@ pub fn save_record(state: State<AppState>) -> Result<String, String> {
|
|||||||
let record = gobang_core::record::GameRecord::from_board(board, "玩家", "对手", None);
|
let record = gobang_core::record::GameRecord::from_board(board, "玩家", "对手", None);
|
||||||
serde_json::to_string_pretty(&record).map_err(|e| e.to_string())
|
serde_json::to_string_pretty(&record).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn host_game(port: u16, state: State<AppState>, app: tauri::AppHandle) -> Result<u16, String> {
|
||||||
|
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||||
|
let (event_tx, event_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let mut network = NetworkLoop::new_server(cmd_rx, event_tx);
|
||||||
|
let sock = std::net::UdpSocket::bind(format!("0.0.0.0:{}", port))
|
||||||
|
.map_err(|e| format!("绑定端口失败: {}", e))?;
|
||||||
|
let actual_port = sock.local_addr().map_err(|e| e.to_string())?.port();
|
||||||
|
drop(sock);
|
||||||
|
|
||||||
|
*state.network_tx.lock().map_err(|e| e.to_string())? = Some(cmd_tx);
|
||||||
|
|
||||||
|
let protocol_id: u64 = 7777;
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let _ = network.run("", protocol_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// event 转发线程
|
||||||
|
let app_clone = app.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for event in event_rx {
|
||||||
|
match event {
|
||||||
|
NetworkEvent::RemoteMove { x, y } => {
|
||||||
|
let _ = app_clone.emit("remote-move", serde_json::json!({ "x": x, "y": y }));
|
||||||
|
}
|
||||||
|
NetworkEvent::RemoteUndo { steps } => {
|
||||||
|
let _ = app_clone.emit("remote-undo", steps);
|
||||||
|
}
|
||||||
|
NetworkEvent::RemoteResign => {
|
||||||
|
let _ = app_clone.emit("remote-resign", ());
|
||||||
|
}
|
||||||
|
NetworkEvent::Connected | NetworkEvent::ClientConnected => {
|
||||||
|
let _ = app_clone.emit("connection-status", "connected");
|
||||||
|
}
|
||||||
|
NetworkEvent::ClientDisconnected => {
|
||||||
|
let _ = app_clone.emit("connection-status", "disconnected");
|
||||||
|
}
|
||||||
|
NetworkEvent::Error(msg) => {
|
||||||
|
let _ = app_clone.emit("network-error", msg);
|
||||||
|
}
|
||||||
|
NetworkEvent::Listening(port) => {
|
||||||
|
let _ = app_clone.emit("listening-port", port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(actual_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn join_game(address: String, state: State<AppState>, app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||||
|
let (event_tx, event_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let mut network = NetworkLoop::new_client(cmd_rx, event_tx);
|
||||||
|
|
||||||
|
*state.network_tx.lock().map_err(|e| e.to_string())? = Some(cmd_tx);
|
||||||
|
|
||||||
|
let protocol_id: u64 = 7777;
|
||||||
|
let addr = address.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let _ = network.run(&addr, protocol_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
let app_clone = app.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for event in event_rx {
|
||||||
|
match event {
|
||||||
|
NetworkEvent::RemoteMove { x, y } => {
|
||||||
|
let _ = app_clone.emit("remote-move", serde_json::json!({ "x": x, "y": y }));
|
||||||
|
}
|
||||||
|
NetworkEvent::RemoteUndo { steps } => {
|
||||||
|
let _ = app_clone.emit("remote-undo", steps);
|
||||||
|
}
|
||||||
|
NetworkEvent::RemoteResign => {
|
||||||
|
let _ = app_clone.emit("remote-resign", ());
|
||||||
|
}
|
||||||
|
NetworkEvent::Connected | NetworkEvent::ClientConnected => {
|
||||||
|
let _ = app_clone.emit("connection-status", "connected");
|
||||||
|
}
|
||||||
|
NetworkEvent::ClientDisconnected => {
|
||||||
|
let _ = app_clone.emit("connection-status", "disconnected");
|
||||||
|
}
|
||||||
|
NetworkEvent::Error(msg) => {
|
||||||
|
let _ = app_clone.emit("network-error", msg);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn send_move(x: usize, y: usize, turn: u32, state: State<AppState>) -> Result<(), String> {
|
||||||
|
let tx = state.network_tx.lock().map_err(|e| e.to_string())?;
|
||||||
|
let tx = tx.as_ref().ok_or("未建立网络连接")?;
|
||||||
|
tx.send(NetworkCmd::SendMove { x, y, turn }).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn send_undo(steps: u32, state: State<AppState>) -> Result<(), String> {
|
||||||
|
let tx = state.network_tx.lock().map_err(|e| e.to_string())?;
|
||||||
|
let tx = tx.as_ref().ok_or("未建立网络连接")?;
|
||||||
|
tx.send(NetworkCmd::SendUndo { steps }).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn send_resign(state: State<AppState>) -> Result<(), String> {
|
||||||
|
let tx = state.network_tx.lock().map_err(|e| e.to_string())?;
|
||||||
|
let tx = tx.as_ref().ok_or("未建立网络连接")?;
|
||||||
|
tx.send(NetworkCmd::SendResign).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|||||||
+5
-1
@@ -12,11 +12,15 @@ pub fn run() {
|
|||||||
commands::new_game,
|
commands::new_game,
|
||||||
commands::place_piece,
|
commands::place_piece,
|
||||||
commands::undo,
|
commands::undo,
|
||||||
|
|
||||||
commands::ai_move,
|
commands::ai_move,
|
||||||
commands::get_game_state,
|
commands::get_game_state,
|
||||||
commands::resign,
|
commands::resign,
|
||||||
commands::save_record,
|
commands::save_record,
|
||||||
|
commands::host_game,
|
||||||
|
commands::join_game,
|
||||||
|
commands::send_move,
|
||||||
|
commands::send_undo,
|
||||||
|
commands::send_resign,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
Reference in New Issue
Block a user