Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer: Impl game initiation #674

Merged
merged 1 commit into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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