Implement generic online system #82

Merged
CoCo_Sol merged 14 commits from impl-online into main 2024-03-25 05:45:17 +00:00
6 changed files with 77 additions and 87 deletions
Showing only changes of commit 89861980fa - Show all commits

View file

@ -2,8 +2,7 @@
use bevnet::Uuid;
use bevy::prelude::*;
use bevy::utils::HashMap;
use networking::GameRank;
use networking::PlayerRank;
use serde::{Deserialize, Serialize};
pub mod camera;
@ -27,15 +26,14 @@ pub enum CurrentScene {
}
/// A player in the game.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[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: GameRank,
}
pub rank: PlayerRank,
/// All the players in the game.
#[derive(Resource, Default)]
pub struct AllPlayers(pub HashMap<Uuid, Player>);
/// The uuid of the player.
pub uuid: Uuid,
}

View file

@ -1,11 +1,11 @@
//! All the code related to the connection.
use bevnet::{Connection, NetworkAppExt, Receive, SendTo, Uuid};
use bevnet::{Connection, NetworkAppExt, Receive, SendTo};
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use super::GameRank;
use crate::{AllPlayers, CurrentScene, Player};
use super::PlayerRank;
use crate::{CurrentScene, Player};
/// A plugin that manage connections (add, remove).
pub struct ConnectionPlugin;
@ -15,7 +15,10 @@ impl Plugin for ConnectionPlugin {
app.add_network_event::<RequestJoin>()
.add_network_event::<AddPlayer>()
.add_network_event::<RemovePlayer>()
.add_systems(Update, (accept_connection, handle_change_player));
.add_systems(
Update,
(accept_connection, handle_new_player, handle_remove_player),
);
}
}
@ -25,32 +28,21 @@ pub struct RequestJoin(pub Player);
/// An event that is trigger when a new player is added.
#[derive(Event, Serialize, Deserialize)]
pub struct AddPlayer(Uuid, Player);
pub struct AddPlayer(Player);
/// An event that is trigger when a player is removed.
#[derive(Event, Serialize, Deserialize)]
pub struct RemovePlayer(pub Uuid);
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: Res<AllPlayers>,
all_players_query: Query<&Player>,
mut requests_join_event: EventReader<Receive<RequestJoin>>,
mut add_players_event: EventWriter<SendTo<AddPlayer>>,
connection: Res<Connection>,
state: Res<State<CurrentScene>>,
CoCo_Sol marked this conversation as resolved Outdated

Pas compris pk il doit être admin ni qui est le joueur en question

Pas compris pk il doit être admin ni qui est le joueur en question
) {
// Check if the player is an admin
if all_players
.get_by_connection(&connection)
.map(|player| player.rank == GameRank::Admin)
!= Some(true)
{
return;
}
for request_join in requests_join_event.read() {
let new_uuid = request_join.0;
let mut new_player = request_join.1.0.clone();
let current_state = *state.get();
@ -58,42 +50,46 @@ pub fn accept_connection(
if current_state == CurrentScene::Menu {
return;
} else if current_state == CurrentScene::Game {
new_player.rank = GameRank::Spectator;
new_player.rank = PlayerRank::Spectator;
}
add_players_event.send(SendTo(new_uuid, AddPlayer(new_uuid, new_player.clone())));
add_players_event.send(SendTo(new_player.uuid, AddPlayer(new_player.clone())));
for (uuid, player) in all_players.0.iter() {
for old_player in all_players_query.iter() {
// Link all players
add_players_event.send(SendTo(*uuid, AddPlayer(new_uuid, new_player.clone())));
add_players_event.send(SendTo(new_uuid, AddPlayer(*uuid, player.clone())));
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 / remove players when a events.
pub fn handle_change_player(
mut add_players: EventReader<Receive<AddPlayer>>,
/// A fonction that handle new players when a events is received.
pub fn handle_new_player(mut add_players: EventReader<Receive<AddPlayer>>, 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<Receive<RemovePlayer>>,
mut all_players: ResMut<AllPlayers>,
mut commands: Commands,
all_players_query: Query<(Entity, &Player)>,
connection: Res<Connection>,
mut next_scene: ResMut<NextState<CurrentScene>>,
) {
let Some(connection) = connection.identifier() else {
return;
};
for add_player in add_players.read() {
println!("{:?}", add_player.1.1);
all_players.0.insert(add_player.1.0, add_player.1.1.clone());
}
for remove_player in remove_players.read() {
if remove_player.1.0 == connection {
all_players.0.clear();
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;
}
all_players.0.remove(&remove_player.1.0);
for (entity, player) in all_players_query.iter() {
if remove_player.1.0.uuid == player.uuid {
commands.entity(entity).despawn();
}
}
}
}

View file

@ -1,11 +1,10 @@
//! All the code related to the networking.
use bevnet::{Connection, NetworkPlugin};
use bevnet::NetworkPlugin;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use self::connection::ConnectionPlugin;
use crate::{AllPlayers, Player};
pub mod connection;
@ -15,28 +14,19 @@ 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)
.init_resource::<AllPlayers>();
.add_plugins(ConnectionPlugin);
}
}
/// The rank of the player.
#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Copy, Debug)]
pub enum GameRank {
/// The player is a spectator. He does not play the game.
pub enum PlayerRank {
/// A spectator. He does not play the game, just renderer the game.
Spectator,
/// The player is an admin. He can remove players.
/// An admin. He manages the game and play the game.
Admin,
CoCo_Sol marked this conversation as resolved Outdated

Es ce que l'admin peut aussi jouer ?

Es ce que l'admin peut aussi jouer ?
/// The player is a player. He can join the game and play.
/// The player. He can join the game and play.
Player,
}
impl AllPlayers {
/// Get the player by a connection.
pub fn get_by_connection(&self, connection: &Connection) -> Option<&Player> {
let uuid = connection.identifier()?;
self.0.get(&uuid)
}
}

View file

@ -5,8 +5,8 @@ use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};
use crate::networking::connection::RemovePlayer;
use crate::networking::GameRank;
use crate::{AllPlayers, CurrentScene};
use crate::networking::PlayerRank;
use crate::{CurrentScene, Player};
/// The plugin for the lobby.
pub struct LobbyPlugin;
@ -22,9 +22,17 @@ fn lobby_ui(
mut ctx: EguiContexts,
mut next_scene: ResMut<NextState<CurrentScene>>,
connection: Res<Connection>,
all_players: Res<AllPlayers>,
all_players_query: Query<&Player>,
mut kick_player: EventWriter<SendTo<RemovePlayer>>,
) {
// 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");
@ -32,7 +40,7 @@ fn lobby_ui(
ui.label("Game created");
ui.horizontal(|ui| {
if all_players.get_by_connection(&connection).map(|p| p.rank) != Some(GameRank::Admin) {
if self_player.rank != PlayerRank::Admin {
return;
}
ui.label("Game ID: ");
@ -42,22 +50,20 @@ fn lobby_ui(
ui.separator();
for (connected_player_id, connected_player) in all_players.0.iter() {
ui.label(connected_player.name.to_string());
if all_players.get_by_connection(&connection).map(|p| p.rank) == Some(GameRank::Admin)
&& connected_player.rank != GameRank::Admin
for player in all_players_query.iter() {
ui.label(player.name.to_string());
if self_player.rank == PlayerRank::Admin
&& player.rank != PlayerRank::Admin
&& ui.button("Remove").clicked()
{
for sender_id in all_players.0.keys() {
kick_player.send(SendTo(*sender_id, RemovePlayer(*connected_player_id)));
for sender_id in all_players_query.iter() {
kick_player.send(SendTo(sender_id.uuid, RemovePlayer(player.clone())));
}
}
ui.separator();
}
if all_players.get_by_connection(&connection).map(|p| p.rank) == Some(GameRank::Admin)
&& ui.button("Run the game").clicked()
{
if self_player.rank == PlayerRank::Admin && ui.button("Run the game").clicked() {
next_scene.set(CurrentScene::Game);
// TODO: run the game
}

View file

@ -5,8 +5,8 @@ use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};
use crate::networking::connection::RequestJoin;
use crate::networking::GameRank;
use crate::{AllPlayers, CurrentScene, Player};
use crate::networking::PlayerRank;
use crate::{CurrentScene, Player};
/// The plugin for the menu.
pub struct MenuPlugin;
@ -16,6 +16,7 @@ impl Plugin for MenuPlugin {
app.add_systems(Update, menu_ui.run_if(in_state(CurrentScene::Menu)));
}
}
/// Display the UI of the menu to host a game or join one.
fn menu_ui(
mut ctx: EguiContexts,
@ -23,8 +24,8 @@ fn menu_ui(
mut next_scene: ResMut<NextState<CurrentScene>>,
mut request_join: EventWriter<SendTo<RequestJoin>>,
mut name: Local<String>,
mut all_player: ResMut<AllPlayers>,
connection: Res<Connection>,
mut commands: Commands,
) {
let Some(uuid) = connection.identifier() else {
return;
@ -55,7 +56,8 @@ fn menu_ui(
game_id,
RequestJoin(Player {
name: name.clone(),
rank: GameRank::Player,
rank: PlayerRank::Player,
uuid,
}),
));
}
@ -65,13 +67,11 @@ fn menu_ui(
if ui.button("Create new game").clicked() {
next_scene.set(CurrentScene::Lobby);
all_player.0.insert(
uuid,
Player {
commands.spawn(Player {
name: name.clone(),
rank: GameRank::Admin,
},
);
rank: PlayerRank::Admin,
uuid,
});
}
});
}

View file

@ -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)?;