Live location tracking and playback for the game "manhunt"

Simplify commands with AppState methods

bwc9876.dev b69803a4 f602f385

verified
+71 -54
+60 -54
backend/src/lib.rs
··· 65 65 } 66 66 } 67 67 68 + pub fn get_menu(&self) -> Result<&PlayerProfile> { 69 + match self { 70 + AppState::Menu(player_profile) => Ok(player_profile), 71 + _ => Err("Not on menu screen".to_string()), 72 + } 73 + } 74 + 75 + pub fn get_menu_mut(&mut self) -> Result<&mut PlayerProfile> { 76 + match self { 77 + AppState::Menu(player_profile) => Ok(player_profile), 78 + _ => Err("Not on menu screen".to_string()), 79 + } 80 + } 81 + 82 + pub fn get_lobby(&self) -> Result<Arc<Lobby>> { 83 + match self { 84 + AppState::Lobby(lobby) => Ok(lobby.clone()), 85 + _ => Err("Not on lobby screen".to_string()), 86 + } 87 + } 88 + 89 + pub fn get_game(&self) -> Result<Arc<Game>> { 90 + match self { 91 + AppState::Game(game) => Ok(game.clone()), 92 + _ => Err("Not on game screen".to_string()), 93 + } 94 + } 95 + 68 96 pub fn start_lobby( 69 97 &mut self, 70 98 join_code: Option<String>, ··· 142 170 143 171 #[tauri::command] 144 172 #[specta::specta] 173 + /// (Screen: Menu) Get the user's player profile 174 + async fn get_profile(state: State<'_, AppStateHandle>) -> Result<PlayerProfile> { 175 + let state = state.read().await; 176 + let profile = state.get_menu()?; 177 + Ok(profile.clone()) 178 + } 179 + 180 + #[tauri::command] 181 + #[specta::specta] 145 182 /// (Screen: Menu) Update the player's profile and persist it 146 183 async fn update_profile( 147 184 new_profile: PlayerProfile, ··· 150 187 ) -> Result { 151 188 new_profile.write_to_store(&app); 152 189 let mut state = state.write().await; 153 - if let AppState::Menu(profile) = &mut *state { 154 - *profile = new_profile; 155 - Ok(()) 156 - } else { 157 - Err("Profile can only be updated on Menu screen".to_string()) 158 - } 190 + let profile = state.get_menu_mut()?; 191 + *profile = new_profile; 192 + Ok(()) 159 193 } 160 194 161 195 #[tauri::command] ··· 179 213 #[specta::specta] 180 214 /// (Screen: Lobby) Get the current state of the lobby, call after receiving an update event 181 215 async fn get_lobby_state(state: State<'_, AppStateHandle>) -> Result<LobbyState> { 182 - let state = state.read().await; 183 - if let AppState::Lobby(lobby) = &*state { 184 - Ok(lobby.clone_state().await) 185 - } else { 186 - Err("Must be called on Lobby screen".to_string()) 187 - } 216 + let lobby = state.read().await.get_lobby()?; 217 + Ok(lobby.clone_state().await) 188 218 } 189 219 190 220 #[tauri::command] 191 221 #[specta::specta] 192 222 /// (Screen: Lobby) Switch teams between seekers and hiders, returns the new [LobbyState] 193 223 async fn switch_teams(seeker: bool, state: State<'_, AppStateHandle>) -> Result<LobbyState> { 194 - let state = state.read().await; 195 - if let AppState::Lobby(lobby) = &*state { 196 - lobby.switch_teams(seeker).await; 197 - Ok(lobby.clone_state().await) 198 - } else { 199 - Err("Must be called on Lobby screen".to_string()) 200 - } 224 + let lobby = state.read().await.get_lobby()?; 225 + lobby.switch_teams(seeker).await; 226 + Ok(lobby.clone_state().await) 201 227 } 202 228 203 229 #[tauri::command] ··· 208 234 settings: GameSettings, 209 235 state: State<'_, AppStateHandle>, 210 236 ) -> Result<LobbyState> { 211 - let state = state.read().await; 212 - if let AppState::Lobby(lobby) = &*state { 213 - lobby.update_settings(settings).await; 214 - Ok(lobby.clone_state().await) 215 - } else { 216 - Err("Must be called on Lobby screen".to_string()) 217 - } 237 + let lobby = state.read().await.get_lobby()?; 238 + lobby.update_settings(settings).await; 239 + Ok(lobby.clone_state().await) 218 240 } 219 241 220 242 #[tauri::command] ··· 222 244 /// (Screen: Lobby) HOST ONLY: Start the game, stops anyone else from joining and switched screen 223 245 /// to AppScreen::Game. 224 246 async fn host_start_game(state: State<'_, AppStateHandle>) -> Result { 225 - let state = state.read().await; 226 - if let AppState::Lobby(lobby) = &*state { 227 - lobby.start_game().await; 228 - Ok(()) 229 - } else { 230 - Err("Must be called on Lobby screen".to_string()) 231 - } 247 + state.read().await.get_lobby()?.start_game().await; 248 + Ok(()) 232 249 } 233 250 234 251 // AppScreen::Game COMMANDS ··· 237 254 #[specta::specta] 238 255 /// (Screen: Game) Mark this player as caught, this player will become a seeker. Returns the new game state 239 256 async fn mark_caught(state: State<'_, AppStateHandle>) -> Result<GameState> { 240 - let state = state.read().await; 241 - if let AppState::Game(game) = &*state { 242 - game.mark_caught().await; 243 - Ok(game.clone_state().await) 244 - } else { 245 - Err("Must be called on Game screen".to_string()) 246 - } 257 + let game = state.read().await.get_game()?; 258 + game.mark_caught().await; 259 + Ok(game.clone_state().await) 247 260 } 248 261 249 262 #[tauri::command] ··· 251 264 /// (Screen: Game) Grab a powerup on the map, this should be called when the user is *in range* of 252 265 /// the powerup. Returns the new game state after rolling for the powerup 253 266 async fn grab_powerup(state: State<'_, AppStateHandle>) -> Result<GameState> { 254 - let state = state.read().await; 255 - if let AppState::Game(game) = &*state { 256 - game.get_powerup().await; 257 - Ok(game.clone_state().await) 258 - } else { 259 - Err("Must be called on Game screen".to_string()) 260 - } 267 + let game = state.read().await.get_game()?; 268 + game.get_powerup().await; 269 + Ok(game.clone_state().await) 261 270 } 262 271 263 272 #[tauri::command] ··· 265 274 /// (Screen: Game) Use the currently held powerup in the player's held_powerup. Does nothing if the 266 275 /// player has none. Returns the updated game state 267 276 async fn use_powerup(state: State<'_, AppStateHandle>) -> Result<GameState> { 268 - let state = state.read().await; 269 - if let AppState::Game(game) = &*state { 270 - game.use_powerup().await; 271 - Ok(game.clone_state().await) 272 - } else { 273 - Err("Must be called on Game screen".to_string()) 274 - } 277 + let game = state.read().await.get_game()?; 278 + game.use_powerup().await; 279 + Ok(game.clone_state().await) 275 280 } 276 281 277 282 pub fn mk_specta() -> tauri_specta::Builder { 278 283 tauri_specta::Builder::<tauri::Wry>::new().commands(collect_commands![ 279 284 start_lobby, 285 + get_profile, 280 286 quit_game_or_lobby, 281 287 get_current_screen, 282 288 update_profile,
+11
frontend/src/bindings.ts
··· 24 24 } 25 25 }, 26 26 /** 27 + * (Screen: Menu) Get the user's player profile 28 + */ 29 + async getProfile(): Promise<Result<PlayerProfile, string>> { 30 + try { 31 + return { status: "ok", data: await TAURI_INVOKE("get_profile") }; 32 + } catch (e) { 33 + if (e instanceof Error) throw e; 34 + else return { status: "error", error: e as any }; 35 + } 36 + }, 37 + /** 27 38 * Quit a running game or leave a lobby 28 39 */ 29 40 async quitGameOrLobby(): Promise<Result<null, string>> {