library

Online co-op book organization video game
git clone https://git.bradleygannon.com/library.git
Log | Files | Refs

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 }