From 79092ea0f55b98337f8af0c0dd4276d9f3823bdd Mon Sep 17 00:00:00 2001 From: CoCo_Sol Date: Tue, 19 Mar 2024 15:08:41 +0000 Subject: [PATCH 01/21] Improve selection system (#78) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #66 Reviewed-on: https://git.tipragot.fr/fish-canard/border-wars/pulls/78 Reviewed-by: Raphaël --- crates/border-wars/src/main.rs | 4 +- crates/border-wars/src/map/mod.rs | 2 +- crates/border-wars/src/map/renderer.rs | 4 +- .../map/{click_tile.rs => selected_tile.rs} | 62 +++++++++++++------ 4 files changed, 49 insertions(+), 23 deletions(-) rename crates/border-wars/src/map/{click_tile.rs => selected_tile.rs} (67%) diff --git a/crates/border-wars/src/main.rs b/crates/border-wars/src/main.rs index cc2c72e..ac4d412 100644 --- a/crates/border-wars/src/main.rs +++ b/crates/border-wars/src/main.rs @@ -2,8 +2,8 @@ use bevy::prelude::*; use border_wars::camera::CameraPlugin; -use border_wars::map::click_tile::TilesClickable; use border_wars::map::renderer::RendererPlugin; +use border_wars::map::selected_tile::SelectTilePlugin; use border_wars::scenes::ScenesPlugin; fn main() { @@ -12,6 +12,6 @@ fn main() { .add_plugins(ScenesPlugin) .add_plugins(RendererPlugin) .add_plugins(CameraPlugin) - .add_plugins(TilesClickable) + .add_plugins(SelectTilePlugin) .run(); } diff --git a/crates/border-wars/src/map/mod.rs b/crates/border-wars/src/map/mod.rs index a5bfdf0..839b7e8 100644 --- a/crates/border-wars/src/map/mod.rs +++ b/crates/border-wars/src/map/mod.rs @@ -1,9 +1,9 @@ //! Contains all the logic related to the map. -pub mod click_tile; pub mod generation; pub mod hex; pub mod renderer; +pub mod selected_tile; use bevy::prelude::*; diff --git a/crates/border-wars/src/map/renderer.rs b/crates/border-wars/src/map/renderer.rs index 915d3c6..d391c72 100644 --- a/crates/border-wars/src/map/renderer.rs +++ b/crates/border-wars/src/map/renderer.rs @@ -21,7 +21,7 @@ impl Plugin for RendererPlugin { /// The gap between the center of the tiles in the map. #[derive(Resource)] -struct TilesGap(Vec2); +pub struct TilesGap(pub Vec2); /// The size of the tiles in the map. #[derive(Resource, Clone, Copy)] @@ -85,7 +85,7 @@ fn render_map( commands.entity(entity).insert(SpriteBundle { sprite: Sprite { - anchor: Anchor::BottomLeft, + anchor: Anchor::BottomCenter, ..default() }, texture, diff --git a/crates/border-wars/src/map/click_tile.rs b/crates/border-wars/src/map/selected_tile.rs similarity index 67% rename from crates/border-wars/src/map/click_tile.rs rename to crates/border-wars/src/map/selected_tile.rs index fb66845..2dcb0e4 100644 --- a/crates/border-wars/src/map/click_tile.rs +++ b/crates/border-wars/src/map/selected_tile.rs @@ -1,15 +1,10 @@ -//! All programs related to the clicking on a tile. +//! All programs related to the selection of a tile. use bevy::prelude::*; +use super::renderer::TilesGap; use super::Tile; -/// The event that is triggered when a tile is clicked. -/// -/// The event contains the index (ID) of the clicked tile. -#[derive(Event)] -pub struct TileJustClicked(pub u32); - /// An event that is triggered when a mouse button is clicked. /// /// The event contains the position of the cursor in the world. @@ -21,15 +16,37 @@ struct ClickOnTheWorld(Vec2); #[derive(Component)] pub struct ZoneNotClickable; -/// A plugin that handles the selection of tiles. -pub struct TilesClickable; +/// The currently selected tile. +#[derive(Resource, Default, Debug)] +pub enum SelectedTile { + /// The entity of the selected tile. + Tile(Entity), -impl Plugin for TilesClickable { + /// Zero tile selected. + #[default] + None, +} + +impl SelectedTile { + /// Returns the entity of the selected tile. + /// Returns `None` if no tile is selected. + pub const fn get_entity(&self) -> Option { + match self { + Self::Tile(entity) => Some(*entity), + Self::None => None, + } + } +} + +/// A plugin that handles the selection of tiles. +pub struct SelectTilePlugin; + +impl Plugin for SelectTilePlugin { fn build(&self, app: &mut App) { app.add_systems(PreUpdate, mouse_handler) .add_systems(PreUpdate, select_closest_tile) .add_event::() - .add_event::(); + .init_resource::(); } } @@ -73,25 +90,30 @@ fn mouse_handler( events_writer.send(ClickOnTheWorld(cursor_position_in_world)); } -/// Get the closest tile to the cursor and send it in an event. +/// Get the closest tile to the cursor and select it. fn select_closest_tile( tiles: Query<(Entity, &Transform, &Tile)>, mut click_event_reader: EventReader, - mut clicked_tile_event_writer: EventWriter, + tile_gap: Res, + mut current_entity: ResMut, ) { for click_event in click_event_reader.read() { - // The closest tile and its distance to the cursor. + // The closest tile and its position. let mut closest_entity: Option = None; let mut closest_position: Option = None; + // To keep the aspect ratio. + let click_position = click_event.0 / tile_gap.0; + for (tile_entity, tile_transform, tile_type) in tiles.iter() { - let mut tile_position = tile_transform.translation.truncate(); let tile_size = tile_type.get_image_size(); let tile_scale = tile_transform.scale.truncate(); - tile_position += (tile_size / 2.0) * tile_scale; + let mut tile_position = tile_transform.translation.truncate() / tile_gap.0; + // The origine of the tile is the bottom center. + tile_position.y += (tile_size.y / 2.0) * tile_scale.y / tile_gap.0.y; - let distance_to_cursor = tile_position.distance(click_event.0); + let distance_to_cursor = tile_position.distance(click_position); if closest_position.is_none() || closest_position > Some(distance_to_cursor) { closest_entity = Some(tile_entity); @@ -99,7 +121,11 @@ fn select_closest_tile( } } if let Some(tile_entity) = closest_entity { - clicked_tile_event_writer.send(TileJustClicked(tile_entity.index())); + if current_entity.get_entity() == Some(tile_entity) { + *current_entity = SelectedTile::None; + } else { + *current_entity = SelectedTile::Tile(tile_entity); + } } } } From 04c376d5eb6a5e84c35535adeff6528f2511a523 Mon Sep 17 00:00:00 2001 From: CoCo_Sol Date: Sun, 24 Mar 2024 20:58:49 +0000 Subject: [PATCH 02/21] Add the MapGenerationPlugin to the main app (#83) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.tipragot.fr/fish-canard/border-wars/pulls/83 Reviewed-by: Raphaël --- crates/border-wars/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/border-wars/src/main.rs b/crates/border-wars/src/main.rs index ac4d412..8329562 100644 --- a/crates/border-wars/src/main.rs +++ b/crates/border-wars/src/main.rs @@ -2,6 +2,7 @@ use bevy::prelude::*; 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::scenes::ScenesPlugin; @@ -13,5 +14,6 @@ fn main() { .add_plugins(RendererPlugin) .add_plugins(CameraPlugin) .add_plugins(SelectTilePlugin) + .add_plugins(MapGenerationPlugin) .run(); } From 5848272f4a0fef7b72b2f436accc45b4a083d1e7 Mon Sep 17 00:00:00 2001 From: CoCo_Sol007 Date: Mon, 25 Mar 2024 05:45:14 +0000 Subject: [PATCH 03/21] 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)?; From 6614a1f056b00385839fcf2f5171cddcfa4a979a Mon Sep 17 00:00:00 2001 From: CoCo_Sol007 Date: Tue, 26 Mar 2024 10:07:12 +0000 Subject: [PATCH 04/21] Remove unless todo (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.tipragot.fr/fish-canard/border-wars/pulls/85 Reviewed-by: Raphaël Co-authored-by: CoCo_Sol007 Co-committed-by: CoCo_Sol007 --- crates/border-wars/src/scenes/lobby.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/border-wars/src/scenes/lobby.rs b/crates/border-wars/src/scenes/lobby.rs index 89201c9..b734f2c 100644 --- a/crates/border-wars/src/scenes/lobby.rs +++ b/crates/border-wars/src/scenes/lobby.rs @@ -44,7 +44,6 @@ fn lobby_ui( return; } ui.label("Game ID: "); - // TODO : get the game ID and display it. ui.text_edit_singleline(&mut connection.identifier().unwrap_or_default().to_string()); }); From fa78c56049a42522188f5ba9edc2518b0b9bf3a5 Mon Sep 17 00:00:00 2001 From: CoCo_Sol007 Date: Sat, 30 Mar 2024 14:41:57 +0000 Subject: [PATCH 05/21] Add check connection system (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.tipragot.fr/fish-canard/border-wars/pulls/84 Reviewed-by: Raphaël Co-authored-by: CoCo_Sol007 Co-committed-by: CoCo_Sol007 --- crates/border-wars/src/lib.rs | 2 +- .../src/networking/check_connection.rs | 118 ++++++++++++++++++ crates/border-wars/src/networking/mod.rs | 7 +- 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 crates/border-wars/src/networking/check_connection.rs diff --git a/crates/border-wars/src/lib.rs b/crates/border-wars/src/lib.rs index a97d7ec..6f144ae 100644 --- a/crates/border-wars/src/lib.rs +++ b/crates/border-wars/src/lib.rs @@ -26,7 +26,7 @@ pub enum CurrentScene { } /// A player in the game. -#[derive(Serialize, Deserialize, Clone, Debug, Component, Resource)] +#[derive(Serialize, Deserialize, Clone, Debug, Component, Resource, PartialEq, Eq, Hash)] pub struct Player { /// The name of the player. pub name: String, diff --git a/crates/border-wars/src/networking/check_connection.rs b/crates/border-wars/src/networking/check_connection.rs new file mode 100644 index 0000000..53c05f2 --- /dev/null +++ b/crates/border-wars/src/networking/check_connection.rs @@ -0,0 +1,118 @@ +//! All the code related to the check connection (check every X seconds if any +//! player is still connected). + +use std::time::Instant; + +use bevnet::{Connection, NetworkAppExt, Receive, SendTo}; +use bevy::prelude::*; +use bevy::utils::HashMap; +use serde::{Deserialize, Serialize}; + +use crate::Player; + +/// A plugin that check if a player is still connected. +pub struct CheckConnectionPlugin; + +/// An event that is trigger when a player is disconnected. +#[derive(Event)] +pub struct PlayerDisconnected(pub Player); + +/// An event that is send between all players to check if a player is still +/// connected. +#[derive(Event, Serialize, Deserialize)] +struct IAmConnected(Player); + +impl Plugin for CheckConnectionPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + Update, + ( + check_connection, + send_check_connection, + handle_disconnect_player, + ), + ) + .add_event::() + .add_network_event::(); + } +} + +/// The interval to check if a player is still connected. +/// We put this into a const because we don't want to change it. +const CHECK_CONNECTION_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5); + +/// A fonction that check if a player is still connected. +fn check_connection( + all_players_query: Query<&Player>, + mut disconnect_event: EventWriter, + mut checked_players: Local>, + mut connect_event: EventReader>, +) { + for Receive(_, IAmConnected(player)) in connect_event.read() { + checked_players.insert(player.clone(), Instant::now()); + } + for player in all_players_query.iter() { + if !(*checked_players).contains_key(player) { + checked_players.insert(player.clone(), Instant::now()); + } + + let Some(last_seen) = (*checked_players).get_mut(player) else { + continue; + }; + if last_seen.elapsed() > CHECK_CONNECTION_INTERVAL { + disconnect_event.send(PlayerDisconnected(player.clone())); + checked_players.remove(player); + } + } +} + +/// A simple time/instant that implement Default. +struct Time(std::time::Instant); + +impl Default for Time { + fn default() -> Self { + Self(std::time::Instant::now()) + } +} + +/// A fonction that send a check connection event to all players. +fn send_check_connection( + mut check_connection_event: EventWriter>, + all_players_query: Query<&Player>, + connection: Res, + mut timer: Local