chess_tui/server/
game_server.rs1use std::{
2 io::{Read, Write},
3 net::{TcpListener, TcpStream},
4 sync::{
5 atomic::{AtomicBool, Ordering},
6 mpsc, Arc, Mutex,
7 },
8 thread,
9};
10
11use crate::constants::{
12 NETWORK_BUFFER_SIZE, NETWORK_PORT, SLEEP_DURATION_LONG_MS, SLEEP_DURATION_SHORT_MS,
13};
14use log;
15
16#[derive(Debug)]
17pub struct Client {
18 addr: String,
19 stream: TcpStream,
20}
21
22#[derive(Clone)]
23pub struct GameServer {
24 pub clients: Arc<Mutex<Vec<Client>>>,
25 pub client_id: usize,
26 pub is_host_white: bool,
27 pub stop_signal: Arc<AtomicBool>,
28}
29
30impl GameServer {
31 pub fn new(is_host_white: bool) -> Self {
32 Self {
33 clients: Arc::new(Mutex::new(vec![])),
34 client_id: 0,
35 is_host_white,
36 stop_signal: Arc::new(AtomicBool::new(false)),
37 }
38 }
39
40 pub fn run(&self) {
41 log::info!("Starting game server on 0.0.0.0:{}", NETWORK_PORT);
42 let listener = match TcpListener::bind(format!("0.0.0.0:{NETWORK_PORT}")) {
43 Ok(l) => l,
44 Err(e) => {
45 log::error!("Failed to create listener: {e}");
46 return;
47 }
48 };
49 if let Err(e) = listener.set_nonblocking(true) {
50 log::error!("Failed to set listener to non-blocking: {e}");
51 return;
52 }
53
54 let state = self.clients.clone();
55 let stop_signal = self.stop_signal.clone();
56 let (shutdown_tx, shutdown_rx) = mpsc::channel();
57
58 let stop_signal_clone = stop_signal.clone();
60 thread::spawn(move || {
61 while !stop_signal_clone.load(Ordering::SeqCst) {
62 thread::sleep(std::time::Duration::from_millis(SLEEP_DURATION_LONG_MS));
63 }
64 let _ = shutdown_tx.send(());
65 });
66
67 loop {
68 if shutdown_rx.try_recv().is_ok() {
70 log::info!("Received shutdown signal, stopping server");
71 break;
72 }
73
74 match listener.accept() {
76 Ok((mut stream, addr)) => {
77 log::info!("New connection from: {}", addr);
78 let state = Arc::clone(&state);
79 let stop_signal = Arc::clone(&stop_signal);
80 let color = if self.is_host_white { "w" } else { "b" };
81
82 thread::spawn(move || {
83 {
84 let Ok(mut state_lock) = state.lock() else {
85 log::error!("Failed to acquire state lock");
86 return;
87 };
88 if state_lock.len() == 1 {
90 if stream.write_all(color.as_bytes()).is_err() {
91 log::error!("Failed to write color to stream");
92 return;
93 }
94 let Some(other_player) = state_lock.last() else {
95 log::error!("No other player found");
96 return;
97 };
98 let Ok(mut other_player_stream) = other_player.stream.try_clone()
99 else {
100 log::error!("Failed to clone other player stream");
101 return;
102 };
103 if other_player_stream.write_all(b"s").is_err() {
104 log::error!("Failed to notify other player of game start");
105 }
106 } else if state_lock.len() >= 2 {
107 let _ = stream.write_all(b"Game is already full");
108 return;
109 }
110
111 let Ok(addr) = stream.peer_addr() else {
112 log::error!("Failed to get peer address");
113 return;
114 };
115 let Ok(stream_clone) = stream.try_clone() else {
116 log::error!("Failed to clone stream");
117 return;
118 };
119 state_lock.push(Client {
120 addr: addr.to_string(),
121 stream: stream_clone,
122 });
123 }
124 handle_client(state, stop_signal, stream);
125 });
126 }
127 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
128 thread::sleep(std::time::Duration::from_millis(SLEEP_DURATION_LONG_MS));
129 }
130 Err(e) => {
131 log::error!("Failed to accept connection: {}", e);
132 }
133 }
134 }
135 }
136}
137
138fn handle_client(
139 state: Arc<Mutex<Vec<Client>>>,
140 stop_signal: Arc<AtomicBool>,
141 mut stream: TcpStream,
142) {
143 let addr = match stream.peer_addr() {
144 Ok(a) => a.to_string(),
145 Err(e) => {
146 log::error!("Failed to get peer address: {e}");
147 return;
148 }
149 };
150 log::info!("Starting client handler for: {}", addr);
151
152 if let Err(e) = stream.set_nonblocking(true) {
154 log::error!("Failed to set non-blocking mode for client {}: {}", addr, e);
155 return;
156 }
157
158 loop {
159 let mut buffer = [0; NETWORK_BUFFER_SIZE];
160 match stream.read(&mut buffer) {
161 Ok(0) => {
162 log::info!("Client {} disconnected", addr);
163 broadcast_message(state.clone(), "ended".to_string(), &addr);
164 remove_client(&state, &addr);
165 stop_signal.store(true, Ordering::SeqCst);
166 break;
167 }
168 Ok(bytes_read) => {
169 let request = String::from_utf8_lossy(&buffer[..bytes_read]);
170 log::debug!("Received message from {}: {}", addr, request.trim());
171 broadcast_message(state.clone(), format!("{}", request), &addr);
172
173 if request.trim() == "ended" {
174 log::info!("Client {} sent end signal", addr);
175 remove_client(&state, &addr);
176 stop_signal.store(true, Ordering::SeqCst);
177 break;
178 }
179 }
180 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
181 std::thread::sleep(std::time::Duration::from_millis(SLEEP_DURATION_SHORT_MS));
183 continue;
184 }
185 Err(e) => {
186 log::error!("Error reading from client {}: {}", addr, e);
187 break;
188 }
189 }
190 }
191}
192
193fn broadcast_message(state: Arc<Mutex<Vec<Client>>>, message: String, sender_addr: &String) {
194 let Ok(state) = state.lock() else {
195 log::error!("Failed to acquire state lock for broadcast");
196 return;
197 };
198 for client in &*state {
199 if &client.addr == sender_addr {
200 continue;
201 }
202 if let Ok(mut client_stream) = client.stream.try_clone() {
203 let _ = client_stream.write_all(message.as_bytes());
204 }
205 }
206}
207
208fn remove_client(state: &Arc<Mutex<Vec<Client>>>, addr: &str) {
209 let Ok(mut state_lock) = state.lock() else {
210 log::error!("Failed to acquire state lock for client removal");
211 return;
212 };
213 if let Some(index) = state_lock.iter().position(|client| client.addr == addr) {
214 state_lock.remove(index);
215 }
216}