···395 available_powerup: self.available_powerup,
396 pings: self.pings.clone(),
397 game_started: self.game_started,
0398 last_global_ping: self.last_global_ping,
399 held_powerup: self.held_powerup,
400 seekers_started: self.seekers_started,
···428 pings: HashMap<Uuid, PlayerPing>,
429 /// When the game was started **in UTC**
430 game_started: UtcDT,
00431 /// The last time all hiders were pinged **in UTC**
432 last_global_ping: Option<UtcDT>,
433 /// The [PowerUpType] the local player is holding
···395 available_powerup: self.available_powerup,
396 pings: self.pings.clone(),
397 game_started: self.game_started,
398+ game_ended: self.game_ended,
399 last_global_ping: self.last_global_ping,
400 held_powerup: self.held_powerup,
401 seekers_started: self.seekers_started,
···429 pings: HashMap<Uuid, PlayerPing>,
430 /// When the game was started **in UTC**
431 game_started: UtcDT,
432+ /// When the game ended, when this is Option::Some, the game has ended
433+ game_ended: Option<UtcDT>,
434 /// The last time all hiders were pinged **in UTC**
435 last_global_ping: Option<UtcDT>,
436 /// The [PowerUpType] the local player is holding
+11-16
backend/src/lib.rs
···3mod lobby;
4mod location;
5mod profile;
06mod transport;
78use std::{collections::HashMap, sync::Arc, time::Duration};
···11use history::AppGameHistory;
12use lobby::{Lobby, LobbyState, StartGameInfo};
13use location::TauriLocation;
14-use log::{error, warn};
15use profile::PlayerProfile;
16-use reqwest::StatusCode;
17use serde::{Deserialize, Serialize};
18use tauri::{AppHandle, Manager, State};
19use tauri_specta::{collect_commands, collect_events, Event};
···6061const GAME_TICK_RATE: Duration = Duration::from_secs(1);
6263-pub const fn server_url() -> &'static str {
64- if let Some(url) = option_env!("APP_SERVER_URL") {
65- url
66- } else {
67- "ws://localhost:3536"
68- }
69-}
70-71/// The app is changing screens, contains the screen it's switching to
72#[derive(Serialize, Deserialize, Clone, Debug, specta::Type, tauri_specta::Event)]
73struct ChangeScreen(AppScreen);
···104 state_updates,
105 ));
106 *self = AppState::Game(game.clone(), profiles.clone());
0107 tokio::spawn(async move {
108 let res = game.main_loop().await;
109 let app2 = app.clone();
···212 let host = join_code.is_none();
213 let room_code = join_code.unwrap_or_else(generate_join_code);
214 let lobby = Arc::new(Lobby::new(
215- server_url(),
216 &room_code,
217 host,
218 profile.clone(),
···228 let mut state = state_handle.write().await;
229 match res {
230 Ok((my_id, start)) => {
0231 state.start_game(app_game, my_id, start).await;
232 }
233 Err(why) => {
···344#[specta::specta]
345/// (Screen: Menu) Check if a room code is valid to join, use this before starting a game
346/// for faster error checking.
347-async fn check_room_code(code: &str) -> Result<bool, String> {
348- let url = format!("{}/room_exists/{code}", server_url());
349- reqwest::get(url)
350 .await
351- .map(|resp| resp.status() == StatusCode::OK)
352 .map_err(|err| err.to_string())
353}
354···523524 tauri::Builder::default()
525 .plugin(tauri_plugin_notification::init())
526- .plugin(tauri_plugin_log::Builder::new().build())
0000527 .plugin(tauri_plugin_opener::init())
528 .plugin(tauri_plugin_geolocation::init())
529 .plugin(tauri_plugin_store::Builder::default().build())
···3mod lobby;
4mod location;
5mod profile;
6+mod server;
7mod transport;
89use std::{collections::HashMap, sync::Arc, time::Duration};
···12use history::AppGameHistory;
13use lobby::{Lobby, LobbyState, StartGameInfo};
14use location::TauriLocation;
15+use log::{error, info, warn, LevelFilter};
16use profile::PlayerProfile;
017use serde::{Deserialize, Serialize};
18use tauri::{AppHandle, Manager, State};
19use tauri_specta::{collect_commands, collect_events, Event};
···6061const GAME_TICK_RATE: Duration = Duration::from_secs(1);
620000000063/// The app is changing screens, contains the screen it's switching to
64#[derive(Serialize, Deserialize, Clone, Debug, specta::Type, tauri_specta::Event)]
65struct ChangeScreen(AppScreen);
···96 state_updates,
97 ));
98 *self = AppState::Game(game.clone(), profiles.clone());
99+ Self::emit_screen_change(&app, AppScreen::Game);
100 tokio::spawn(async move {
101 let res = game.main_loop().await;
102 let app2 = app.clone();
···205 let host = join_code.is_none();
206 let room_code = join_code.unwrap_or_else(generate_join_code);
207 let lobby = Arc::new(Lobby::new(
0208 &room_code,
209 host,
210 profile.clone(),
···220 let mut state = state_handle.write().await;
221 match res {
222 Ok((my_id, start)) => {
223+ info!("Starting game as {my_id}");
224 state.start_game(app_game, my_id, start).await;
225 }
226 Err(why) => {
···337#[specta::specta]
338/// (Screen: Menu) Check if a room code is valid to join, use this before starting a game
339/// for faster error checking.
340+async fn check_room_code(code: &str) -> Result<bool> {
341+ server::room_exists(code)
0342 .await
0343 .map_err(|err| err.to_string())
344}
345···514515 tauri::Builder::default()
516 .plugin(tauri_plugin_notification::init())
517+ .plugin(
518+ tauri_plugin_log::Builder::new()
519+ .level(LevelFilter::Debug)
520+ .build(),
521+ )
522 .plugin(tauri_plugin_opener::init())
523 .plugin(tauri_plugin_geolocation::init())
524 .plugin(tauri_plugin_store::Builder::default().build())
···10dev:
11 cargo tauri dev
12000013# Format everything
14fmt:
15 cargo fmt
···10dev:
11 cargo tauri dev
1213+# Start a webview window *without* running the frontend, only one frontend needs to run at once
14+dev-window:
15+ cargo run -p manhunt-app
16+17# Format everything
18fmt:
19 cargo fmt