From 5848272f4a0fef7b72b2f436accc45b4a083d1e7 Mon Sep 17 00:00:00 2001 From: CoCo_Sol007 Date: Mon, 25 Mar 2024 05:45:14 +0000 Subject: [PATCH] Implement generic online system (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit note that the feature of adding a system to check if players are still connected will be in a new pull request. Reviewed-on: https://git.tipragot.fr/fish-canard/border-wars/pulls/82 Reviewed-by: Raphaƫl Co-authored-by: CoCo_Sol007 Co-committed-by: CoCo_Sol007 --- Cargo.lock | 10 +- crates/border-wars/Cargo.toml | 2 + crates/border-wars/src/lib.rs | 17 ++++ crates/border-wars/src/main.rs | 2 + .../border-wars/src/networking/connection.rs | 95 +++++++++++++++++++ crates/border-wars/src/networking/mod.rs | 32 +++++++ crates/border-wars/src/scenes/lobby.rs | 41 +++++++- crates/border-wars/src/scenes/menu.rs | 40 +++++++- crates/relay-client/src/lib.rs | 2 +- 9 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 crates/border-wars/src/networking/connection.rs create mode 100644 crates/border-wars/src/networking/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6ac8535..ee4f5e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1314,10 +1314,12 @@ dependencies = [ name = "border-wars" version = "0.1.0" dependencies = [ + "bevnet", "bevy", "bevy_egui", "noise", "paste", + "serde", ] [[package]] @@ -3805,18 +3807,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", diff --git a/crates/border-wars/Cargo.toml b/crates/border-wars/Cargo.toml index fc84f96..fabf283 100644 --- a/crates/border-wars/Cargo.toml +++ b/crates/border-wars/Cargo.toml @@ -15,3 +15,5 @@ bevy = "0.12.1" bevy_egui = "0.24.0" noise = "0.8.2" paste = "1.0.14" +bevnet = { path = "../bevnet" } +serde = "1.0.197" diff --git a/crates/border-wars/src/lib.rs b/crates/border-wars/src/lib.rs index 257aefb..a97d7ec 100644 --- a/crates/border-wars/src/lib.rs +++ b/crates/border-wars/src/lib.rs @@ -1,9 +1,13 @@ //! The file that contains utility functions, enums, structs for the game. +use bevnet::Uuid; use bevy::prelude::*; +use networking::PlayerRank; +use serde::{Deserialize, Serialize}; pub mod camera; pub mod map; +pub mod networking; pub mod responsive_scale; pub mod scenes; @@ -20,3 +24,16 @@ pub enum CurrentScene { /// When we play this wonderful game. Game, } + +/// A player in the game. +#[derive(Serialize, Deserialize, Clone, Debug, Component, Resource)] +pub struct Player { + /// The name of the player. + pub name: String, + + /// The rank of the player. + pub rank: PlayerRank, + + /// The uuid of the player. + pub uuid: Uuid, +} diff --git a/crates/border-wars/src/main.rs b/crates/border-wars/src/main.rs index 8329562..ea58352 100644 --- a/crates/border-wars/src/main.rs +++ b/crates/border-wars/src/main.rs @@ -5,6 +5,7 @@ use border_wars::camera::CameraPlugin; use border_wars::map::generation::MapGenerationPlugin; use border_wars::map::renderer::RendererPlugin; use border_wars::map::selected_tile::SelectTilePlugin; +use border_wars::networking::NetworkingPlugin; use border_wars::scenes::ScenesPlugin; fn main() { @@ -14,6 +15,7 @@ fn main() { .add_plugins(RendererPlugin) .add_plugins(CameraPlugin) .add_plugins(SelectTilePlugin) + .add_plugins(NetworkingPlugin) .add_plugins(MapGenerationPlugin) .run(); } diff --git a/crates/border-wars/src/networking/connection.rs b/crates/border-wars/src/networking/connection.rs new file mode 100644 index 0000000..ee47512 --- /dev/null +++ b/crates/border-wars/src/networking/connection.rs @@ -0,0 +1,95 @@ +//! All the code related to the connection. + +use bevnet::{Connection, NetworkAppExt, Receive, SendTo}; +use bevy::prelude::*; +use serde::{Deserialize, Serialize}; + +use super::PlayerRank; +use crate::{CurrentScene, Player}; + +/// A plugin that manage connections (add, remove). +pub struct ConnectionPlugin; + +impl Plugin for ConnectionPlugin { + fn build(&self, app: &mut App) { + app.add_network_event::() + .add_network_event::() + .add_network_event::() + .add_systems( + Update, + (accept_connection, handle_new_player, handle_remove_player), + ); + } +} + +/// An event that is trigger when a new player request to join a game. +#[derive(Event, Serialize, Deserialize)] +pub struct RequestJoin(pub Player); + +/// An event that is trigger when a new player is added. +#[derive(Event, Serialize, Deserialize)] +pub struct AddPlayer(Player); + +/// An event that is trigger when a player is removed. +#[derive(Event, Serialize, Deserialize)] +pub struct RemovePlayer(pub Player); + +/// A fonction that accept new connection. +/// It add the player to the list of all players. +pub fn accept_connection( + all_players_query: Query<&Player>, + mut requests_join_event: EventReader>, + mut add_players_event: EventWriter>, + state: Res>, +) { + for request_join in requests_join_event.read() { + let mut new_player = request_join.1.0.clone(); + + let current_state = *state.get(); + + if current_state == CurrentScene::Menu { + return; + } else if current_state == CurrentScene::Game { + new_player.rank = PlayerRank::Spectator; + } + + add_players_event.send(SendTo(new_player.uuid, AddPlayer(new_player.clone()))); + + for old_player in all_players_query.iter() { + // Link all players + add_players_event.send(SendTo(old_player.uuid, AddPlayer(new_player.clone()))); + add_players_event.send(SendTo(new_player.uuid, AddPlayer(old_player.clone()))); + } + } +} + +/// A fonction that handle new players when a events is received. +pub fn handle_new_player(mut add_players: EventReader>, mut commands: Commands) { + for add_player in add_players.read() { + commands.spawn(add_player.1.0.clone()); + } +} + +/// A fonction that handle remove players when a events is received. +pub fn handle_remove_player( + mut remove_players: EventReader>, + mut commands: Commands, + all_players_query: Query<(Entity, &Player)>, + connection: Res, + mut next_scene: ResMut>, +) { + for remove_player in remove_players.read() { + if Some(remove_player.1.0.uuid) == connection.identifier() { + next_scene.set(CurrentScene::Menu); + all_players_query.iter().for_each(|(entity, _)| { + commands.entity(entity).despawn(); + }); + return; + } + for (entity, player) in all_players_query.iter() { + if remove_player.1.0.uuid == player.uuid { + commands.entity(entity).despawn(); + } + } + } +} diff --git a/crates/border-wars/src/networking/mod.rs b/crates/border-wars/src/networking/mod.rs new file mode 100644 index 0000000..e3f4f8a --- /dev/null +++ b/crates/border-wars/src/networking/mod.rs @@ -0,0 +1,32 @@ +//! All the code related to the networking. + +use bevnet::NetworkPlugin; +use bevy::prelude::*; +use serde::{Deserialize, Serialize}; + +use self::connection::ConnectionPlugin; + +pub mod connection; + +/// The plugin for the networking. +pub struct NetworkingPlugin; + +impl Plugin for NetworkingPlugin { + fn build(&self, app: &mut App) { + app.add_plugins(NetworkPlugin::new("relay.cocosol.fr".to_string())) + .add_plugins(ConnectionPlugin); + } +} + +/// The rank of the player. +#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Copy, Debug)] +pub enum PlayerRank { + /// A spectator. He does not play the game, just renderer the game. + Spectator, + + /// An admin. He manages the game and play the game. + Admin, + + /// The player. He can join the game and play. + Player, +} diff --git a/crates/border-wars/src/scenes/lobby.rs b/crates/border-wars/src/scenes/lobby.rs index 84a0348..89201c9 100644 --- a/crates/border-wars/src/scenes/lobby.rs +++ b/crates/border-wars/src/scenes/lobby.rs @@ -1,9 +1,12 @@ //! The lobby of the game. +use bevnet::{Connection, SendTo}; use bevy::prelude::*; use bevy_egui::{egui, EguiContexts}; -use crate::CurrentScene; +use crate::networking::connection::RemovePlayer; +use crate::networking::PlayerRank; +use crate::{CurrentScene, Player}; /// The plugin for the lobby. pub struct LobbyPlugin; @@ -15,7 +18,21 @@ impl Plugin for LobbyPlugin { } /// Display the UI of the lobby. -fn lobby_ui(mut ctx: EguiContexts, mut next_scene: ResMut>) { +fn lobby_ui( + mut ctx: EguiContexts, + mut next_scene: ResMut>, + connection: Res, + all_players_query: Query<&Player>, + mut kick_player: EventWriter>, +) { + // Get our player info. + let Some(self_player) = all_players_query + .iter() + .find(|player| connection.identifier() == Some(player.uuid)) + else { + return; + }; + egui::CentralPanel::default().show(ctx.ctx_mut(), |ui| { ui.heading("Border Wars"); @@ -23,14 +40,30 @@ fn lobby_ui(mut ctx: EguiContexts, mut next_scene: ResMut, mut next_scene: ResMut>, + mut request_join: EventWriter>, + mut name: Local, + connection: Res, + mut commands: Commands, ) { + let Some(uuid) = connection.identifier() else { + return; + }; + egui::CentralPanel::default().show(ctx.ctx_mut(), |ui| { ui.heading("Border Wars"); ui.separator(); + ui.label("Name"); + ui.text_edit_singleline(&mut *name); + + ui.separator(); + ui.label("Connect to an existing game:"); ui.horizontal(|ui| { ui.label("Game ID: "); ui.text_edit_singleline(&mut *connection_string); + let Ok(game_id) = Uuid::parse_str(&connection_string) else { + return; + }; + if ui.button("Join").clicked() { - next_scene.set(CurrentScene::Game); - // TODO: connect to the game + next_scene.set(CurrentScene::Lobby); + request_join.send(SendTo( + game_id, + RequestJoin(Player { + name: name.clone(), + rank: PlayerRank::Player, + uuid, + }), + )); } }); @@ -39,7 +67,11 @@ fn menu_ui( if ui.button("Create new game").clicked() { next_scene.set(CurrentScene::Lobby); - // TODO: create a new game + commands.spawn(Player { + name: name.clone(), + rank: PlayerRank::Admin, + uuid, + }); } }); } diff --git a/crates/relay-client/src/lib.rs b/crates/relay-client/src/lib.rs index b052784..b390d21 100644 --- a/crates/relay-client/src/lib.rs +++ b/crates/relay-client/src/lib.rs @@ -80,7 +80,7 @@ impl Connection { path.push(".relay-data"); // Check if the file exists. - match path.exists() { + match false { true => { // Read the file and parse the identifier and secret key. let contents = fs::read(&path)?;