From 3c03767da22186b0488471c116534372ff1d6866 Mon Sep 17 00:00:00 2001 From: CoCo_Sol007 Date: Mon, 25 Mar 2024 21:27:02 +0100 Subject: [PATCH] save --- crates/border-wars/src/lib.rs | 2 +- .../src/networking/check_connection.rs | 128 ++++++++++++++++++ crates/border-wars/src/networking/mod.rs | 7 +- 3 files changed, 134 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..9302946 --- /dev/null +++ b/crates/border-wars/src/networking/check_connection.rs @@ -0,0 +1,128 @@ +//! TODO + +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. +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 timer. +struct Timer(std::time::Duration, std::time::Instant); + +impl Timer { + /// Create a new timer. + fn new(duration: std::time::Duration) -> Self { + Self(duration, std::time::Instant::now()) + } + + /// Check if the timer is finished. + fn is_finished(&self) -> bool { + self.1.elapsed() >= self.0 + } +} + +impl Default for Timer { + fn default() -> Self { + Self::new(CHECK_CONNECTION_INTERVAL / 2) + } +} + +/// 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, +) { + if !timer.is_finished() { + return; + } + let Some(self_player) = all_players_query + .iter() + .find(|player| connection.identifier() == Some(player.uuid)) + else { + return; + }; + + for player in all_players_query.iter() { + check_connection_event.send(SendTo(player.uuid, IAmConnected(self_player.clone()))); + } + + timer.1 = std::time::Instant::now(); +} + +/// A fonction that handle player disconnection. +fn handle_disconnect_player( + mut disconnect_players: EventReader, + all_players_query: Query<(&Player, Entity)>, + mut commands: Commands, +) { + for PlayerDisconnected(disconnect_player) in disconnect_players.read() { + let Some((_, entity)) = all_players_query + .iter() + .find(|(player, _entity)| *player == disconnect_player) + else { + continue; + }; + + commands.entity(entity).despawn(); + } +} diff --git a/crates/border-wars/src/networking/mod.rs b/crates/border-wars/src/networking/mod.rs index e3f4f8a..197eab5 100644 --- a/crates/border-wars/src/networking/mod.rs +++ b/crates/border-wars/src/networking/mod.rs @@ -4,8 +4,10 @@ use bevnet::NetworkPlugin; use bevy::prelude::*; use serde::{Deserialize, Serialize}; +use self::check_connection::CheckConnectionPlugin; use self::connection::ConnectionPlugin; +pub mod check_connection; pub mod connection; /// The plugin for the networking. @@ -14,12 +16,13 @@ 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); + .add_plugins(ConnectionPlugin) + .add_plugins(CheckConnectionPlugin); } } /// The rank of the player. -#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Copy, Debug, Hash)] pub enum PlayerRank { /// A spectator. He does not play the game, just renderer the game. Spectator,