Skip to main content

chess_tui/server/
game_server.rs

1use 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        // Spawn a thread to watch for the stop signal
59        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            // Check for shutdown signal
69            if shutdown_rx.try_recv().is_ok() {
70                log::info!("Received shutdown signal, stopping server");
71                break;
72            }
73
74            // Handle incoming connections
75            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                            // There is already one player (host who choose the color) we will need to send the color to the joining player and inform the host of the game start
89                            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    // Set socket to non-blocking mode
153    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                // This is normal for non-blocking sockets
182                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}