Skip to content

Commit

Permalink
Multiplayer: Impl game initiation (#674)
Browse files Browse the repository at this point in the history
  • Loading branch information
Indy2222 authored Aug 18, 2023
1 parent 25dd74f commit 49ce374
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 10 deletions.
6 changes: 5 additions & 1 deletion crates/core/src/gconfig.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};

use bevy::prelude::Resource;
use tinyvec::ArrayVec;
use tinyvec::{array_vec, ArrayVec};

use crate::player::{Player, PlayerRange};

Expand Down Expand Up @@ -51,6 +51,10 @@ impl LocalPlayers {
Self::new(playable, locals.collect())
}

pub fn from_single(playable: Player) -> Self {
Self::new(playable, array_vec!(_ => playable))
}

/// # Arguments
///
/// * `playable` - the player controlled locally by the user.
Expand Down
2 changes: 1 addition & 1 deletion crates/map/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl MapHash {
}

/// Constructs the map hash from a hexadecimal string.
pub(crate) fn from_hex(hex: &str) -> Result<Self, HexError> {
pub fn from_hex(hex: &str) -> Result<Self, HexError> {
if hex.len() != 64 {
return Err(HexError::InvalidLenError);
}
Expand Down
1 change: 1 addition & 0 deletions crates/menu/src/multiplayer/joined/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bevy::prelude::*;

pub(super) use self::state::LocalPlayerRes;
use self::{state::JoinedGameStatePlugin, ui::JoinedGameUiPlugin};

mod state;
Expand Down
97 changes: 93 additions & 4 deletions crates/menu/src/multiplayer/joined/state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
use bevy::prelude::*;
use de_core::state::AppState;
use de_core::{
assets::asset_path,
gconfig::{GameConfig, LocalPlayers},
player::Player,
state::AppState,
};
use de_gui::ToastEvent;
use de_lobby_client::GetGameRequest;
use de_multiplayer::{PeerJoinedEvent, PeerLeftEvent, ShutdownMultiplayerEvent};
use de_lobby_model::GameMap;
use de_map::hash::MapHash;
use de_messages::Readiness;
use de_multiplayer::{
GameReadinessEvent, PeerJoinedEvent, PeerLeftEvent, ShutdownMultiplayerEvent,
};

use super::ui::RefreshPlayersEvent;
use crate::multiplayer::{
Expand All @@ -15,21 +25,52 @@ pub(super) struct JoinedGameStatePlugin;

impl Plugin for JoinedGameStatePlugin {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(MultiplayerState::GameJoined), refresh)
app.add_event::<StartGameEvent>()
.add_systems(OnEnter(MultiplayerState::GameJoined), (setup, refresh))
.add_systems(OnExit(MultiplayerState::GameJoined), cleanup)
.add_systems(
Update,
(
refresh
.run_if(on_event::<PeerJoinedEvent>().or_else(on_event::<PeerLeftEvent>())),
handle_get_response,
start
.run_if(on_event::<StartGameEvent>())
.after(handle_get_response),
handle_readiness,
)
.run_if(in_state(MultiplayerState::GameJoined)),
);
}
}

fn cleanup(state: Res<State<AppState>>, mut shutdown: EventWriter<ShutdownMultiplayerEvent>) {
#[derive(Event)]
struct StartGameEvent(GameMap);

#[derive(Resource)]
pub(crate) struct LocalPlayerRes(Player);

impl LocalPlayerRes {
pub(crate) fn new(player: Player) -> Self {
Self(player)
}
}

#[derive(Resource)]
struct ReadyRes(bool);

fn setup(mut commands: Commands) {
commands.insert_resource(ReadyRes(false));
}

fn cleanup(
mut commands: Commands,
state: Res<State<AppState>>,
mut shutdown: EventWriter<ShutdownMultiplayerEvent>,
) {
commands.remove_resource::<LocalPlayerRes>();
commands.remove_resource::<ReadyRes>();

if state.as_ref() != &AppState::InGame {
shutdown.send(ShutdownMultiplayerEvent);
}
Expand All @@ -40,16 +81,36 @@ fn refresh(game_name: Res<GameNameRes>, mut sender: Sender<GetGameRequest>) {
sender.send(GetGameRequest::new(game_name.name_owned()));
}

fn handle_readiness(
mut events: EventReader<GameReadinessEvent>,
game_name: Res<GameNameRes>,
mut sender: Sender<GetGameRequest>,
mut ready: ResMut<ReadyRes>,
) {
if events.iter().all(|e| **e != Readiness::Ready) {
return;
}

sender.send(GetGameRequest::new(game_name.name_owned()));
ready.0 = true;
}

fn handle_get_response(
mut multi_state: ResMut<NextState<MultiplayerState>>,
mut receiver: Receiver<GetGameRequest>,
mut start_events: EventWriter<StartGameEvent>,
ready: Res<ReadyRes>,
mut refresh: EventWriter<RefreshPlayersEvent>,
mut toasts: EventWriter<ToastEvent>,
) {
while let Some(result) = receiver.receive() {
match result {
Ok(game) => {
refresh.send(RefreshPlayersEvent::from_slice(game.players()));

if ready.0 {
start_events.send(StartGameEvent(game.setup().config().map().clone()));
}
}
Err(error) => {
toasts.send(ToastEvent::new(error));
Expand All @@ -58,3 +119,31 @@ fn handle_get_response(
}
}
}

fn start(
mut commands: Commands,
mut events: EventReader<StartGameEvent>,
player: Res<LocalPlayerRes>,
mut app_state: ResMut<NextState<AppState>>,
mut multi_state: ResMut<NextState<MultiplayerState>>,
mut toasts: EventWriter<ToastEvent>,
) {
let Some(event) = events.iter().last() else {
return;
};

let map_path = match MapHash::from_hex(event.0.hash()) {
Ok(hash) => hash.construct_path(asset_path("maps")),
Err(error) => {
toasts.send(ToastEvent::new(error));
multi_state.set(MultiplayerState::SignIn);
return;
}
};

commands.insert_resource(GameConfig::new(
map_path,
LocalPlayers::from_single(player.0),
));
app_state.set(AppState::InGame);
}
5 changes: 5 additions & 0 deletions crates/menu/src/multiplayer/joining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use de_multiplayer::{

use super::{
current::GameNameRes,
joined::LocalPlayerRes,
requests::{Receiver, Sender},
MultiplayerState,
};
Expand All @@ -31,10 +32,12 @@ impl Plugin for JoiningGamePlugin {
}

fn cleanup(
mut commands: Commands,
state: Res<State<MultiplayerState>>,
mut shutdown: EventWriter<ShutdownMultiplayerEvent>,
) {
if state.as_ref() != &MultiplayerState::GameJoined {
commands.remove_resource::<LocalPlayerRes>();
shutdown.send(ShutdownMultiplayerEvent);
}
}
Expand Down Expand Up @@ -67,6 +70,7 @@ fn handle_get_response(
}

fn handle_joined_event(
mut commands: Commands,
game_name: Res<GameNameRes>,
mut events: EventReader<GameJoinedEvent>,
mut sender: Sender<JoinGameRequest>,
Expand All @@ -75,6 +79,7 @@ fn handle_joined_event(
return;
};

commands.insert_resource(LocalPlayerRes::new(event.player()));
sender.send(JoinGameRequest::new(
game_name.name_owned(),
GamePlayerInfo::new(event.player().to_num()),
Expand Down
45 changes: 41 additions & 4 deletions crates/menu/src/multiplayer/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ use de_gui::ToastEvent;
use de_lobby_client::CreateGameRequest;
use de_lobby_model::{GameConfig, GameSetup};
use de_multiplayer::{
ConnectionType, GameOpenedEvent, NetGameConf, ShutdownMultiplayerEvent, StartMultiplayerEvent,
ConnectionType, GameJoinedEvent, GameOpenedEvent, NetGameConf, ShutdownMultiplayerEvent,
StartMultiplayerEvent,
};

use super::{
current::GameNameRes,
joined::LocalPlayerRes,
requests::{Receiver, Sender},
MultiplayerState,
};
Expand All @@ -19,16 +21,24 @@ pub(crate) struct SetupGamePlugin;
impl Plugin for SetupGamePlugin {
fn build(&self, app: &mut App) {
app.add_event::<SetupGameEvent>()
.add_systems(OnEnter(MultiplayerState::GameSetup), setup_network)
.add_systems(OnEnter(MultiplayerState::GameSetup), (setup, setup_network))
.add_systems(OnExit(MultiplayerState::GameSetup), cleanup)
.add_systems(
PreUpdate,
handle_setup_event.run_if(in_state(MenuState::Multiplayer)),
)
.add_systems(
Update,
(create_game_in_lobby, handle_lobby_response)
(
create_game_in_lobby,
handle_lobby_response,
handle_joined_event,
)
.run_if(in_state(MultiplayerState::GameSetup)),
)
.add_systems(
PostUpdate,
move_once_ready.run_if(in_state(MultiplayerState::GameSetup)),
);
}
}
Expand All @@ -52,6 +62,9 @@ impl SetupGameEvent {
#[derive(Resource)]
pub(crate) struct GameConfigRes(GameConfig);

#[derive(Resource)]
struct JoinedRes(bool);

fn handle_setup_event(
mut commands: Commands,
mut next_state: ResMut<NextState<MultiplayerState>>,
Expand All @@ -65,18 +78,34 @@ fn handle_setup_event(
next_state.set(MultiplayerState::GameSetup);
}

fn move_once_ready(
joined: Res<JoinedRes>,
local_player: Option<Res<LocalPlayerRes>>,
mut next_state: ResMut<NextState<MultiplayerState>>,
) {
if joined.0 && local_player.is_some() {
next_state.set(MultiplayerState::GameJoined);
}
}

fn cleanup(
mut commands: Commands,
state: Res<State<MultiplayerState>>,
mut shutdown: EventWriter<ShutdownMultiplayerEvent>,
) {
commands.remove_resource::<GameConfigRes>();
commands.remove_resource::<JoinedRes>();

if state.as_ref() != &MultiplayerState::GameJoined {
commands.remove_resource::<LocalPlayerRes>();
shutdown.send(ShutdownMultiplayerEvent);
}
}

fn setup(mut commands: Commands) {
commands.insert_resource(JoinedRes(false));
}

fn setup_network(
config: Res<Configuration>,
game_config: Res<GameConfigRes>,
Expand Down Expand Up @@ -109,7 +138,15 @@ fn create_game_in_lobby(
sender.send(CreateGameRequest::new(game_setup));
}

fn handle_joined_event(mut commands: Commands, mut events: EventReader<GameJoinedEvent>) {
let Some(event) = events.iter().last() else {
return;
};
commands.insert_resource(LocalPlayerRes::new(event.player()));
}

fn handle_lobby_response(
mut joined: ResMut<JoinedRes>,
mut next_state: ResMut<NextState<MultiplayerState>>,
mut receiver: Receiver<CreateGameRequest>,
mut toasts: EventWriter<ToastEvent>,
Expand All @@ -118,7 +155,7 @@ fn handle_lobby_response(
match result {
Ok(_) => {
info!("Game successfully created.");
next_state.set(MultiplayerState::GameJoined);
joined.0 = true;
}
Err(error) => {
toasts.send(ToastEvent::new(error));
Expand Down

0 comments on commit 49ce374

Please sign in to comment.