Skip to content

Commit

Permalink
Impl multiplayer game readiness staging (#691)
Browse files Browse the repository at this point in the history
  • Loading branch information
Indy2222 authored Aug 18, 2023
1 parent 49ce374 commit 82f6358
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 9 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions crates/core/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ use bevy::{

use crate::gamestate::GameState;

/// This plugin accumulates events received during [`GameState::Prepared`],
/// [`GameState::Loading`] and [`GameState::Waiting`] and re-sends them on
/// enter of [`GameState::Playing`].
pub struct ResendEventPlugin<T: Event> {
_marker: PhantomData<T>,
}
Expand All @@ -24,10 +27,14 @@ impl<T: Event> Default for ResendEventPlugin<T> {

impl<T: Event> Plugin for ResendEventPlugin<T> {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(GameState::Loading), setup::<T>)
app.add_systems(OnEnter(GameState::Prepared), setup::<T>)
.add_systems(
Update,
enqueue_events::<T>.run_if(in_state(GameState::Loading)),
enqueue_events::<T>.run_if(
in_state(GameState::Prepared)
.or_else(in_state(GameState::Loading))
.or_else(in_state(GameState::Waiting)),
),
)
.add_systems(
OnEnter(GameState::Playing),
Expand Down
12 changes: 10 additions & 2 deletions crates/core/src/gamestate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ impl Plugin for GameStateSetupPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
GameStatePlugin,
ProgressPlugin::new(GameState::Loading).continue_to(GameState::Playing),
ProgressPlugin::new(GameState::Loading).continue_to(GameState::Waiting),
));
}
}
Expand All @@ -20,13 +20,21 @@ nested_state!(
enter = setup,
exit = cleanup,
variants = {
// The game is ready for initialization. Waiting for other players to
// get to the state as well.
Prepared,
// The game is being initialized locally and possibly by other players
// as well.
Loading,
// The game is locally initialized, waiting for other player to finish
// initialization as well.
Waiting,
Playing,
}
);

fn setup(mut next_state: ResMut<NextState<GameState>>) {
next_state.set(GameState::Loading);
next_state.set(GameState::Prepared);
}

fn cleanup(mut commands: Commands) {
Expand Down
9 changes: 8 additions & 1 deletion crates/core/src/gconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ use crate::player::{Player, PlayerRange};
#[derive(Resource)]
pub struct GameConfig {
map_path: PathBuf,
multiplayer: bool,
locals: LocalPlayers,
}

impl GameConfig {
pub fn new<P: Into<PathBuf>>(map_path: P, locals: LocalPlayers) -> Self {
pub fn new<P: Into<PathBuf>>(map_path: P, multiplayer: bool, locals: LocalPlayers) -> Self {
Self {
map_path: map_path.into(),
multiplayer,
locals,
}
}
Expand All @@ -25,6 +27,10 @@ impl GameConfig {
self.map_path.as_path()
}

pub fn multiplayer(&self) -> bool {
self.multiplayer
}

pub fn locals(&self) -> &LocalPlayers {
&self.locals
}
Expand Down Expand Up @@ -95,6 +101,7 @@ mod tests {
fn test_game_config() {
let config = GameConfig::new(
"/some/path",
false,
LocalPlayers::from_max_player(Player::Player1, Player::Player4),
);
assert_eq!(config.map_path().to_string_lossy(), "/some/path");
Expand Down
8 changes: 5 additions & 3 deletions crates/loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ categories.workspace = true

[dependencies]
# DE
de_camera.workspace = true
de_conf.workspace = true
de_core.workspace = true
de_map.workspace = true
de_terrain.workspace = true
de_messages.workspace = true
de_multiplayer.workspace = true
de_spawner.workspace = true
de_camera.workspace = true
de_conf.workspace = true
de_terrain.workspace = true

# Other
bevy.workspace = true
Expand Down
6 changes: 5 additions & 1 deletion crates/loader/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use bevy::{app::PluginGroupBuilder, prelude::PluginGroup};
use map::MapLoaderPlugin;
use readiness::ReadinessPlugin;

mod map;
mod readiness;

pub struct LoaderPluginGroup;

impl PluginGroup for LoaderPluginGroup {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>().add(MapLoaderPlugin)
PluginGroupBuilder::start::<Self>()
.add(MapLoaderPlugin)
.add(ReadinessPlugin)
}
}
47 changes: 47 additions & 0 deletions crates/loader/src/readiness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use bevy::prelude::*;
use de_core::{gamestate::GameState, gconfig::GameConfig};
use de_messages::Readiness;
use de_multiplayer::{GameReadinessEvent, SetReadinessEvent};

pub(crate) struct ReadinessPlugin;

impl Plugin for ReadinessPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
OnEnter(GameState::Prepared),
set_readiness(Readiness::Prepared),
)
.add_systems(
OnEnter(GameState::Waiting),
set_readiness(Readiness::Initialized),
)
.add_systems(
Update,
(
progress(Readiness::Prepared, GameState::Loading)
.run_if(in_state(GameState::Prepared)),
progress(Readiness::Initialized, GameState::Playing)
.run_if(in_state(GameState::Waiting)),
),
);
}
}

fn set_readiness(readiness: Readiness) -> impl Fn(EventWriter<SetReadinessEvent>) {
move |mut events: EventWriter<SetReadinessEvent>| {
events.send(SetReadinessEvent::from(readiness));
}
}

fn progress(
readiness: Readiness,
target_state: GameState,
) -> impl Fn(Res<GameConfig>, EventReader<GameReadinessEvent>, ResMut<NextState<GameState>>) {
move |conf: Res<GameConfig>,
mut events: EventReader<GameReadinessEvent>,
mut state: ResMut<NextState<GameState>>| {
if !conf.multiplayer() || events.iter().any(|e| **e == readiness) {
state.set(target_state);
}
}
}
1 change: 1 addition & 0 deletions crates/menu/src/multiplayer/joined/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ fn start(

commands.insert_resource(GameConfig::new(
map_path,
true,
LocalPlayers::from_single(player.0),
));
app_state.set(AppState::InGame);
Expand Down
1 change: 1 addition & 0 deletions crates/menu/src/singleplayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fn button_system(
Some(path) => {
commands.insert_resource(GameConfig::new(
path,
false,
LocalPlayers::from_max_player(Player::Player1, Player::Player4),
));
next_state.set(AppState::InGame);
Expand Down

0 comments on commit 82f6358

Please sign in to comment.