aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/breakout/breakout.rs65
-rw-r--r--examples/breakout/client.rs27
-rw-r--r--examples/breakout/protocol.rs9
-rw-r--r--examples/breakout/server.rs97
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()),