diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/breakout/breakout.rs | 65 | ||||
-rw-r--r-- | examples/breakout/client.rs | 27 | ||||
-rw-r--r-- | examples/breakout/protocol.rs | 9 | ||||
-rw-r--r-- | examples/breakout/server.rs | 97 |
4 files changed, 129 insertions, 69 deletions
diff --git a/examples/breakout/breakout.rs b/examples/breakout/breakout.rs index 767fd9c..e74a8fc 100644 --- a/examples/breakout/breakout.rs +++ b/examples/breakout/breakout.rs @@ -1,4 +1,5 @@ //! A simplified implementation of the classic game "Breakout". +//! => Original example by Bevy, modified for Bevy Quinnet to add a 2 players versus mode. use bevy::{ ecs::schedule::ShouldRun, @@ -6,9 +7,12 @@ use bevy::{ sprite::collide_aabb::{collide, Collision}, time::FixedTimestep, }; -use bevy_quinnet::{client::QuinnetClientPlugin, server::QuinnetServerPlugin}; +use bevy_quinnet::{ + client::QuinnetClientPlugin, + server::{QuinnetServerPlugin, Server}, +}; use client::{handle_server_messages, start_connection}; -use server::{handle_client_messages, handle_server_events, start_listening, Users}; +use server::{handle_client_messages, handle_server_events, start_listening}; mod client; mod protocol; @@ -69,8 +73,8 @@ const COLLISION_SOUND_EFFECT: &str = "sounds/breakout_collision.ogg"; #[derive(Clone, Eq, PartialEq, Debug, Hash)] enum GameState { MainMenu, - Hosting, - Joining, + HostingLobby, + JoiningLobby, Running, } @@ -84,23 +88,29 @@ fn main() { .insert_resource(Scoreboard { score: 0 }) .insert_resource(ClearColor(BACKGROUND_COLOR)) // Resources - .insert_resource(server::Users::default()) //TODO Move ? + .insert_resource(server::Players::default()) //TODO Move ? .insert_resource(client::Users::default()) // Main menu .add_system_set(SystemSet::on_enter(GameState::MainMenu).with_system(setup_main_menu)) .add_system_set(SystemSet::on_update(GameState::MainMenu).with_system(handle_menu_buttons)) .add_system_set(SystemSet::on_exit(GameState::MainMenu).with_system(teardown_main_menu)) // Hosting - .add_system_set(SystemSet::on_enter(GameState::Hosting).with_system(start_listening)) .add_system_set( - SystemSet::on_update(GameState::Hosting) + SystemSet::on_enter(GameState::HostingLobby) + .with_system(start_listening) + .with_system(start_connection), + ) + .add_system_set( + SystemSet::on_update(GameState::HostingLobby) .with_system(handle_client_messages) + .with_system(handle_server_events) + .with_system(handle_server_messages) .with_system(handle_server_events), ) - // or Joining - .add_system_set(SystemSet::on_enter(GameState::Joining).with_system(start_connection)) + // or just Joining + .add_system_set(SystemSet::on_enter(GameState::JoiningLobby).with_system(start_connection)) .add_system_set( - SystemSet::on_update(GameState::Joining) + SystemSet::on_update(GameState::JoiningLobby) .with_system(handle_server_messages) .with_system(handle_server_events), ) @@ -121,6 +131,30 @@ fn main() { .with_system(play_collision_sound.after(check_for_collisions)) .with_system(update_scoreboard), ) + // Every app is a client + .add_system_set( + SystemSet::on_update(GameState::Running) + .with_system(handle_server_messages) + .with_system(handle_server_events), + ) + // But hosting apps are also a server + .add_system_set( + SystemSet::new() + // https://github.com/bevyengine/bevy/issues/1839 + .with_run_criteria(run_if_host.chain( + |In(input): In<ShouldRun>, state: Res<State<GameState>>| match state.current() { + GameState::Running => input, + _ => ShouldRun::No, + }, + )) + // .with_system(check_for_collisions) + // .with_system(move_paddle.before(check_for_collisions)) + // .with_system(apply_velocity.before(check_for_collisions)) + // .with_system(play_collision_sound.after(check_for_collisions)) + // .with_system(update_scoreboard) + .with_system(handle_server_messages) + .with_system(handle_server_events), + ) .add_system(bevy::window::close_on_esc) .run(); } @@ -233,6 +267,13 @@ struct Scoreboard { score: usize, } +fn run_if_host(server: Res<Server>) -> ShouldRun { + match server.is_listening() { + true => ShouldRun::No, + false => ShouldRun::Yes, + } +} + fn setup_main_menu(mut commands: Commands, asset_server: Res<AssetServer>) { // Camera commands.spawn_bundle(Camera2dBundle::default()); @@ -286,8 +327,8 @@ fn handle_menu_buttons( Interaction::Clicked => { *color = PRESSED_BUTTON_COLOR.into(); match item { - MenuItem::Host => game_state.set(GameState::Hosting).unwrap(), - MenuItem::Join => game_state.set(GameState::Joining).unwrap(), + MenuItem::Host => game_state.set(GameState::HostingLobby).unwrap(), + MenuItem::Join => game_state.set(GameState::JoiningLobby).unwrap(), } } Interaction::Hovered => { diff --git a/examples/breakout/client.rs b/examples/breakout/client.rs index 0c723a3..69b5ffa 100644 --- a/examples/breakout/client.rs +++ b/examples/breakout/client.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use bevy::prelude::{warn, EventReader, ResMut}; +use bevy::prelude::{warn, EventReader, ResMut, State}; use bevy_quinnet::{ client::{CertificateVerificationMode, Client, ClientConfigurationData, ConnectionEvent}, ClientId, }; -use crate::protocol::ServerMessage; +use crate::{protocol::ServerMessage, GameState}; #[derive(Debug, Clone, Default)] pub(crate) struct Users { @@ -14,23 +14,28 @@ pub(crate) struct Users { names: HashMap<ClientId, String>, } -pub(crate) fn handle_server_messages(mut client: ResMut<Client>, mut users: ResMut<Users>) { +pub(crate) fn handle_server_messages( + mut client: ResMut<Client>, + mut users: ResMut<Users>, + mut game_state: ResMut<State<GameState>>, +) { while let Ok(Some(message)) = client.receive_message::<ServerMessage>() { match message { - ServerMessage::ClientConnected { client_id: _ } => {} - ServerMessage::ClientDisconnected { client_id } => { - if let Some(username) = users.names.remove(&client_id) { - println!("{} left", username); - } else { - warn!("ClientDisconnected for an unknown client_id: {}", client_id) - } - } + // ServerMessage::ClientConnected { client_id: _ } => {} + // ServerMessage::ClientDisconnected { client_id } => { + // if let Some(username) = users.names.remove(&client_id) { + // println!("{} left", username); + // } else { + // warn!("ClientDisconnected for an unknown client_id: {}", client_id) + // } + // } ServerMessage::InitClient { client_id } => { users.self_id = client_id; } ServerMessage::BrickDestroyed {} => todo!(), ServerMessage::BallPosition {} => todo!(), ServerMessage::PaddlePosition {} => todo!(), + ServerMessage::GameStart {} => game_state.set(GameState::Running).unwrap(), } } } diff --git a/examples/breakout/protocol.rs b/examples/breakout/protocol.rs index 7ed1710..9cb9232 100644 --- a/examples/breakout/protocol.rs +++ b/examples/breakout/protocol.rs @@ -7,17 +7,18 @@ pub enum Input {} // Messages from clients #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ClientMessage { - Join {}, - Disconnect {}, + // Join {}, + // Disconnect {}, PaddleInput {}, } // Messages from the server #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ServerMessage { - ClientConnected { client_id: ClientId }, - ClientDisconnected { client_id: ClientId }, + // ClientConnected { client_id: ClientId }, + // ClientDisconnected { client_id: ClientId }, InitClient { client_id: ClientId }, + GameStart {}, BrickDestroyed {}, BallPosition {}, PaddlePosition {}, diff --git a/examples/breakout/server.rs b/examples/breakout/server.rs index d015269..150a1da 100644 --- a/examples/breakout/server.rs +++ b/examples/breakout/server.rs @@ -1,70 +1,83 @@ use std::collections::HashMap; -use bevy::prelude::{info, warn, EventReader, ResMut}; +use bevy::prelude::{info, warn, EventReader, ResMut, State}; use bevy_quinnet::{ - server::{CertificateRetrievalMode, ConnectionLostEvent, Server, ServerConfigurationData}, + server::{ + CertificateRetrievalMode, ConnectionEvent, ConnectionLostEvent, Server, + ServerConfigurationData, + }, ClientId, }; -use crate::protocol::{ClientMessage, ServerMessage}; +use crate::{ + protocol::{ClientMessage, ServerMessage}, + GameState, +}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct Player { + score: u64, +} #[derive(Debug, Clone, Default)] -pub(crate) struct Users { - names: HashMap<ClientId, String>, +pub(crate) struct Players { + scores: HashMap<ClientId, Player>, } -pub(crate) fn handle_client_messages(mut server: ResMut<Server>, mut users: ResMut<Users>) { +pub(crate) fn handle_client_messages(mut server: ResMut<Server>, mut users: ResMut<Players>) { while let Ok(Some((message, client_id))) = server.receive_message::<ClientMessage>() { match message { - ClientMessage::Join {} => {} - ClientMessage::Disconnect {} => { - // We tell the server to disconnect this user - server.disconnect_client(client_id); - handle_disconnect(&mut server, &mut users, client_id); - } + // ClientMessage::Disconnect {} => { + // // We tell the server to disconnect this user + // server.disconnect_client(client_id); + // handle_disconnect(&mut server, &mut users, client_id); + // } ClientMessage::PaddleInput {} => todo!(), } } } pub(crate) fn handle_server_events( + mut connection_events: EventReader<ConnectionEvent>, mut connection_lost_events: EventReader<ConnectionLostEvent>, mut server: ResMut<Server>, - mut users: ResMut<Users>, + mut players: ResMut<Players>, + mut game_state: ResMut<State<GameState>>, ) { - // The server signals us about users that lost connection - for client in connection_lost_events.iter() { - handle_disconnect(&mut server, &mut users, client.id); + // The server signals us about new connections + for client in connection_events.iter() { + // Refuse connection once we already have two players + if players.scores.len() >= 2 { + server.disconnect_client(client.id) + } else { + players.scores.insert(client.id, Player { score: 0 }); + if players.scores.len() == 2 { + server + .send_group_message( + players.scores.keys().into_iter(), + ServerMessage::GameStart {}, + ) + .unwrap(); + } + } } + // The server signals us about users that lost connection + // for client in connection_lost_events.iter() { + // handle_disconnect(&mut server, &mut players, client.id); + // } } -/// Shared disconnection behaviour, whether the client lost connection or asked to disconnect -pub(crate) fn handle_disconnect( - server: &mut ResMut<Server>, - users: &mut ResMut<Users>, - client_id: ClientId, -) { - // Remove this user - if let Some(username) = users.names.remove(&client_id) { - // Broadcast its deconnection - server - .send_group_message( - users.names.keys().into_iter(), - ServerMessage::ClientDisconnected { - client_id: client_id, - }, - ) - .unwrap(); - info!("{} disconnected", username); - } else { - warn!( - "Received a Disconnect from an unknown or disconnected client: {}", - client_id - ) - } -} +// /// Shared disconnection behaviour, whether the client lost connection or asked to disconnect +// pub(crate) fn handle_disconnect( +// server: &mut ResMut<Server>, +// players: &mut ResMut<Players>, +// client_id: ClientId, +// ) { +// // Remove this user + +// } -pub(crate) fn start_listening(server: ResMut<Server>) { +pub(crate) fn start_listening(mut server: ResMut<Server>) { server .start( ServerConfigurationData::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string()), |