From 5d5abb8f26bc63eeba4cce64ba9a911137006f93 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Fri, 14 Jul 2023 15:47:28 +0200 Subject: [PATCH] Multiplayer: end game on error (#623) --- crates/core/src/gresult.rs | 25 ++++++++++++++++++++++--- crates/menu/src/aftergame.rs | 16 ++++++++++++---- crates/multiplayer/src/lifecycle.rs | 18 ++++++++++++++++-- crates/spawner/src/gameend.rs | 4 ++-- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/crates/core/src/gresult.rs b/crates/core/src/gresult.rs index 3c730f10..052f984e 100644 --- a/crates/core/src/gresult.rs +++ b/crates/core/src/gresult.rs @@ -1,12 +1,31 @@ use bevy::prelude::Resource; #[derive(Resource)] -pub struct GameResult { - won: bool, +pub enum GameResult { + /// Game finished normally with the player either loosing or winning. + Finished(NormalResult), + /// The game finished due to an error. + Error(String), } impl GameResult { - pub fn new(won: bool) -> Self { + /// Create new normally finished game result. + pub fn finished(won: bool) -> Self { + Self::Finished(NormalResult::new(won)) + } + + /// Create game result from an error. + pub fn error(message: impl ToString) -> Self { + Self::Error(message.to_string()) + } +} + +pub struct NormalResult { + won: bool, +} + +impl NormalResult { + fn new(won: bool) -> Self { Self { won } } diff --git a/crates/menu/src/aftergame.rs b/crates/menu/src/aftergame.rs index 62f18bea..e2ccba3f 100644 --- a/crates/menu/src/aftergame.rs +++ b/crates/menu/src/aftergame.rs @@ -18,10 +18,18 @@ fn cleanup(mut commands: Commands) { } fn setup(mut commands: GuiCommands, menu: Res, result: Res) { - let text = if result.won() { - "You have won!" - } else { - "You have lost! " + let text = match result.as_ref() { + GameResult::Finished(result) => { + if result.won() { + "You have won!".to_owned() + } else { + "You have lost!".to_owned() + } + } + GameResult::Error(message) => { + error!("Game finished with an error: {message}"); + format!("Error: {message}") + } }; let text_id = commands.spawn_label(OuterStyle::default(), text).id(); commands.entity(menu.root_node()).add_child(text_id); diff --git a/crates/multiplayer/src/lifecycle.rs b/crates/multiplayer/src/lifecycle.rs index 76b8207f..e5352ca7 100644 --- a/crates/multiplayer/src/lifecycle.rs +++ b/crates/multiplayer/src/lifecycle.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use bevy::prelude::*; -use de_core::state::AppState; +use de_core::{gresult::GameResult, state::AppState}; use de_gui::ToastEvent; use crate::{config::NetGameConf, NetState}; @@ -14,6 +14,11 @@ impl Plugin for LifecyclePlugin { .add_event::() .add_event::() .add_system(cleanup.in_schedule(OnEnter(NetState::None))) + .add_system( + finish_game + .in_schedule(OnEnter(NetState::None)) + .run_if(in_state(AppState::InGame)), + ) .add_system( game_left .in_schedule(OnExit(AppState::InGame)) @@ -26,7 +31,9 @@ impl Plugin for LifecyclePlugin { ) .add_system( shutdown - .run_if(not(in_state(NetState::None))) + .run_if(not( + in_state(NetState::None).or_else(in_state(NetState::ShuttingDown)) + )) .run_if(on_event::()), ) .add_system( @@ -81,6 +88,10 @@ fn cleanup(mut commands: Commands) { commands.remove_resource::(); } +fn finish_game(mut next_state: ResMut>) { + next_state.set(AppState::InMenu); +} + fn start( mut commands: Commands, mut next_state: ResMut>, @@ -99,6 +110,7 @@ fn shutdown(mut next_state: ResMut>) { } fn errors( + mut commands: Commands, mut events: EventReader, mut toasts: EventWriter, mut shutdowns: EventWriter, @@ -111,6 +123,8 @@ fn errors( toasts.send(ToastEvent::new(&event.0)); shutdowns.send(ShutdownMultiplayerEvent); + commands.insert_resource(GameResult::error(&event.0)); + events.clear(); } diff --git a/crates/spawner/src/gameend.rs b/crates/spawner/src/gameend.rs index e14d7964..b9029c17 100644 --- a/crates/spawner/src/gameend.rs +++ b/crates/spawner/src/gameend.rs @@ -26,11 +26,11 @@ fn game_end_detection_system( ) { let mut result = None; if counter.player(conf.locals().playable()).unwrap().total() == 0 { - result = Some(GameResult::new(false)); + result = Some(GameResult::finished(false)); } else if conf.players().all(|player| { conf.locals().is_playable(player) || counter.player(player).unwrap().total() == 0 }) { - result = Some(GameResult::new(true)); + result = Some(GameResult::finished(true)); } if let Some(result) = result {