run.rs (7990B)
1 use std::{ 2 collections::VecDeque, 3 net::SocketAddr, 4 sync::mpsc::channel, 5 thread, 6 time::{Duration, Instant, SystemTime}, 7 }; 8 9 use common::game::{Action, Response, state::GameState}; 10 use macroquad::{ 11 prelude::*, 12 ui::{ 13 hash, root_ui, 14 widgets::{Button, InputText, Label}, 15 }, 16 }; 17 use serde::{Deserialize, Serialize}; 18 19 use crate::{ 20 ClientState, 21 input::{ 22 handle_drop_book_input, handle_movement_input, handle_pick_up_book_input, 23 handle_put_book_on_shelf_input, handle_take_book_off_shelf_input, 24 }, 25 net::handle_connection, 26 render::Camera, 27 state::ViewMode, 28 }; 29 30 const CONFY_APP_NAME: &str = "library-game"; 31 const CONFY_CONFIG_NAME: &str = "config"; 32 33 impl Default for ClientState { 34 fn default() -> Self { 35 Self { 36 mode: ViewMode::default(), 37 camera: Camera::default(), 38 game: GameState::default(), 39 old_game: GameState::default(), 40 interpolated_game: GameState::default(), 41 local_player_id: None, 42 sequence_number: 0, 43 unresponded_actions: VecDeque::new(), 44 interpolation_basis: SystemTime::now(), 45 debug_view: cfg!(debug_assertions), 46 t0: Instant::now(), 47 t1: Instant::now(), 48 dt: Duration::ZERO, 49 tx: None, 50 rx: None, 51 config: confy::load(CONFY_APP_NAME, Some(CONFY_CONFIG_NAME)).unwrap_or_default(), 52 menu_error: String::new(), 53 first_frame: true, 54 selected_inventory_book: 0, 55 } 56 } 57 } 58 59 #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] 60 pub(crate) struct Config { 61 player_name: String, 62 server: String, 63 } 64 65 impl Config { 66 pub(crate) fn player_name(&self) -> String { 67 self.player_name.clone() 68 } 69 } 70 71 impl ClientState { 72 pub(crate) fn run_menu_frame(&mut self) { 73 clear_background(MAGENTA); 74 const ELEMENT_SIZE: Vec2 = Vec2::new(420., 24.); 75 const NUM_ELEMENTS: usize = 4; 76 const MENU_SIZE: Vec2 = Vec2::new(ELEMENT_SIZE.x, ELEMENT_SIZE.y * NUM_ELEMENTS as f32); 77 const ELEMENT_PITCH: Vec2 = Vec2::new(0., ELEMENT_SIZE.y); 78 let screen_size = vec2(screen_width(), screen_height()); 79 let menu_top_left = screen_size / 2. - MENU_SIZE / 2.; 80 let mut ui = root_ui(); 81 let name_input_token = hash!(); 82 InputText::new(name_input_token) 83 .label("name") 84 .position(menu_top_left) 85 .size(ELEMENT_SIZE) 86 .margin(Vec2::ZERO) 87 .ui(&mut ui, &mut self.config.player_name); 88 InputText::new(hash!()) 89 .label("server") 90 .position(menu_top_left) 91 .size(ELEMENT_SIZE) 92 .margin(Vec2::ZERO) 93 .ui(&mut ui, &mut self.config.server); 94 let connect_button_clicked = Button::new("connect") 95 .position(menu_top_left + ELEMENT_PITCH * 2.) 96 .size(ELEMENT_SIZE) 97 .ui(&mut ui); 98 let addr = if connect_button_clicked || is_key_pressed(KeyCode::Enter) { 99 if let Ok(addr) = self.config.server.parse::<SocketAddr>() { 100 Some(addr) 101 } else if self.config.player_name.is_empty() { 102 self.menu_error = "enter your name pls".to_string(); 103 None 104 } else { 105 self.menu_error = "invalid server :-\\".to_string(); 106 None 107 } 108 } else { 109 None 110 }; 111 Label::new(self.menu_error.as_str()) 112 .position(menu_top_left + ELEMENT_PITCH * 3.) 113 .size(ELEMENT_SIZE) 114 .ui(&mut ui); 115 if self.first_frame { 116 ui.set_input_focus(name_input_token); 117 self.first_frame = false; 118 } 119 if let Some(addr) = addr { 120 let _ = confy::store(CONFY_APP_NAME, Some(CONFY_CONFIG_NAME), self.config.clone()); 121 let (client_to_server_tx, client_to_server_rx) = channel(); 122 let (server_to_client_tx, server_to_client_rx) = channel(); 123 thread::spawn(move || { 124 handle_connection(addr, server_to_client_tx, client_to_server_rx) 125 }); 126 self.tx = Some(client_to_server_tx); 127 self.rx = Some(server_to_client_rx); 128 self.mode = ViewMode::Game; 129 } 130 } 131 pub(crate) fn run_game_frame(&mut self) { 132 // Update frame timer so we move the player at the right speed 133 self.tick(); 134 135 // Process any new responses from the server 136 let mut response_count: u8 = 0; 137 let mut latest_sequence_number = 0; 138 if let Some(ref rx) = self.rx { 139 for response in rx.try_iter().collect::<Vec<Response>>() { 140 response_count = response_count.saturating_add(1); 141 // If this is the first response that we're processing during this tick, 142 // then we need to copy the current game state so we can interpolate against 143 // it below 144 if response_count == 1 { 145 self.advance_game_state(); 146 } 147 self.handle_response(&response); 148 latest_sequence_number = latest_sequence_number.max(response.sequence_number()); 149 } 150 } 151 152 // If there were any server responses, then do server reconciliation and client 153 // prediction 154 if response_count > 0 { 155 self.reconcile(latest_sequence_number); 156 self.predict(); 157 } 158 159 if let Some(id) = self.local_player_id 160 && let Some(local_player) = self.game.get_player(&id).cloned() 161 { 162 // Collect and send actions based on user input this frame 163 let mut new_actions = [None, None, None, None, None]; 164 165 new_actions[0] = handle_movement_input(&local_player, &self.dt); 166 new_actions[1] = 167 handle_pick_up_book_input(&local_player, &self.camera, self.game.floor_books()); 168 new_actions[2] = handle_drop_book_input(&local_player, self.selected_inventory_book); 169 new_actions[3] = handle_put_book_on_shelf_input( 170 self.game.bookshelves(), 171 &self.camera, 172 &local_player, 173 self.selected_inventory_book, 174 ); 175 new_actions[4] = handle_take_book_off_shelf_input( 176 self.game.bookshelves(), 177 &self.camera, 178 &local_player, 179 ); 180 181 for new_action in new_actions.iter().filter_map(|na| na.clone()) { 182 let action = Action::new(self.sequence_number, id, new_action); 183 self.game.apply_action(&action); 184 self.send_action(&action); 185 } 186 187 // Center local player on the screen 188 self.follow_local_player(); 189 190 // Entity interpolation 191 self.interpolated_game = self.interpolate(); 192 193 // Toggle debug view if requested 194 if is_key_pressed(KeyCode::F3) { 195 self.debug_view = !self.debug_view; 196 } 197 198 // Set selected book 199 let books_count = local_player.books().len(); 200 if self.selected_inventory_book > books_count.saturating_sub(1) { 201 self.selected_inventory_book = books_count.saturating_sub(1); 202 } 203 if is_key_pressed(KeyCode::Key1) { 204 self.selected_inventory_book = 0; 205 } else if is_key_pressed(KeyCode::Key2) { 206 self.selected_inventory_book = 1; 207 } else if is_key_pressed(KeyCode::Key3) { 208 self.selected_inventory_book = 2; 209 } else if is_key_pressed(KeyCode::Key4) { 210 self.selected_inventory_book = 3; 211 } else if is_key_pressed(KeyCode::Key5) { 212 self.selected_inventory_book = 4; 213 } 214 215 self.draw_frame(); 216 } 217 } 218 }