main.rs (3905B)
1 use std::{ 2 collections::LinkedList, 3 sync::{Arc, Mutex, mpsc::channel}, 4 thread, 5 time::Instant, 6 }; 7 8 use common::game::{Response, ResponseKind, TICK_DURATION, state::GameState}; 9 use structdiff::StructDiff; 10 use uuid::Uuid; 11 12 use crate::net::listen; 13 14 pub mod net; 15 16 const TICKS_PER_FULL_GAME_STATE: u32 = (5. / TICK_DURATION.as_secs_f32()) as u32; 17 18 fn main() { 19 let clients = Arc::new(Mutex::new(LinkedList::new())); 20 let (client_to_main_tx, client_to_main_rx) = channel(); 21 let clients_clone = clients.clone(); 22 let _listener = thread::spawn(|| listen(clients_clone, client_to_main_tx)); 23 let mut next_tick = Instant::now() + TICK_DURATION; 24 let mut previous_game_state; 25 let mut game_state = GameState::default(); 26 let mut full_state_counter = 0; 27 loop { 28 previous_game_state = game_state.clone(); 29 full_state_counter += 1; 30 31 if let Ok(mut c) = clients.try_lock() { 32 // Process all pending player actions 33 for action in client_to_main_rx.try_iter() { 34 if let Some(client) = c 35 .iter_mut() 36 .find(|cl| cl.id.is_some() && cl.id.unwrap() == action.player_id()) 37 { 38 client.sequence_number = action.sequence_number(); 39 game_state.apply_action(&action); 40 } 41 } 42 43 // Clean up players associated with clients that have disconnected 44 for dropped_client in c.extract_if(|client| client.thread.is_finished()) { 45 if let Some(id) = dropped_client.id { 46 game_state.remove_player(&id); 47 } 48 } 49 50 // Set IDs for clients that are too new to have one, and also send those clients the 51 // full current game state 52 for client_without_id in c.iter_mut().filter(|cl| cl.id.is_none()) { 53 let id = Uuid::new_v4(); 54 let _ = client_without_id.tx.send(Response::new( 55 game_state.get_timestamp(), 56 0, 57 ResponseKind::SetId { id }, 58 )); 59 let _ = client_without_id.tx.send(Response::new( 60 game_state.get_timestamp(), 61 0, 62 ResponseKind::ReplaceGameState { 63 new_game_state: game_state.clone(), 64 }, 65 )); 66 client_without_id.id = Some(id); 67 } 68 69 game_state.update_timestamp(); 70 71 // Send full game state if it's been long enough 72 if full_state_counter > TICKS_PER_FULL_GAME_STATE { 73 full_state_counter = 0; 74 for client in c.iter() { 75 let _ = client.tx.send(Response::new( 76 game_state.get_timestamp(), 77 client.sequence_number, 78 ResponseKind::ReplaceGameState { 79 new_game_state: game_state.clone(), 80 }, 81 )); 82 } 83 // Send updates if there are any 84 } else { 85 let diffs = previous_game_state.diff(&game_state); 86 if !diffs.is_empty() { 87 let kind = ResponseKind::UpdateGameState { diffs }; 88 for client in c.iter() { 89 let _ = client.tx.send(Response::new( 90 game_state.get_timestamp(), 91 client.sequence_number, 92 kind.clone(), 93 )); 94 } 95 } 96 } 97 98 // Throttling 99 let now = Instant::now(); 100 if now < next_tick { 101 thread::sleep(next_tick - now); 102 } 103 next_tick = Instant::now() + TICK_DURATION; 104 } 105 } 106 }