generated from tipragot/rust
Implement generic online system #82
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1314,10 +1314,12 @@ dependencies = [
|
||||||
name = "border-wars"
|
name = "border-wars"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bevnet",
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_egui",
|
"bevy_egui",
|
||||||
"noise",
|
"noise",
|
||||||
"paste",
|
"paste",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3805,18 +3807,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.196"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.196"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -15,3 +15,5 @@ bevy = "0.12.1"
|
||||||
bevy_egui = "0.24.0"
|
bevy_egui = "0.24.0"
|
||||||
noise = "0.8.2"
|
noise = "0.8.2"
|
||||||
paste = "1.0.14"
|
paste = "1.0.14"
|
||||||
|
bevnet = { path = "../bevnet" }
|
||||||
|
serde = "1.0.197"
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
//! The file that contains utility functions, enums, structs for the game.
|
//! The file that contains utility functions, enums, structs for the game.
|
||||||
|
|
||||||
|
use bevnet::Uuid;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use networking::PlayerRank;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
|
pub mod networking;
|
||||||
pub mod responsive_scale;
|
pub mod responsive_scale;
|
||||||
pub mod scenes;
|
pub mod scenes;
|
||||||
|
|
||||||
|
@ -20,3 +24,16 @@ pub enum CurrentScene {
|
||||||
/// When we play this wonderful game.
|
/// When we play this wonderful game.
|
||||||
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,
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use border_wars::camera::CameraPlugin;
|
||||||
use border_wars::map::generation::MapGenerationPlugin;
|
use border_wars::map::generation::MapGenerationPlugin;
|
||||||
use border_wars::map::renderer::RendererPlugin;
|
use border_wars::map::renderer::RendererPlugin;
|
||||||
use border_wars::map::selected_tile::SelectTilePlugin;
|
use border_wars::map::selected_tile::SelectTilePlugin;
|
||||||
|
use border_wars::networking::NetworkingPlugin;
|
||||||
use border_wars::scenes::ScenesPlugin;
|
use border_wars::scenes::ScenesPlugin;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -14,6 +15,7 @@ fn main() {
|
||||||
.add_plugins(RendererPlugin)
|
.add_plugins(RendererPlugin)
|
||||||
.add_plugins(CameraPlugin)
|
.add_plugins(CameraPlugin)
|
||||||
.add_plugins(SelectTilePlugin)
|
.add_plugins(SelectTilePlugin)
|
||||||
|
.add_plugins(NetworkingPlugin)
|
||||||
.add_plugins(MapGenerationPlugin)
|
.add_plugins(MapGenerationPlugin)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
95
crates/border-wars/src/networking/connection.rs
Normal file
95
crates/border-wars/src/networking/connection.rs
Normal file
|
@ -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::<RequestJoin>()
|
||||||
|
.add_network_event::<AddPlayer>()
|
||||||
|
.add_network_event::<RemovePlayer>()
|
||||||
|
.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<Receive<RequestJoin>>,
|
||||||
|
mut add_players_event: EventWriter<SendTo<AddPlayer>>,
|
||||||
|
state: Res<State<CurrentScene>>,
|
||||||
CoCo_Sol marked this conversation as resolved
Outdated
|
|||||||
|
) {
|
||||||
|
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<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 commands: Commands,
|
||||||
|
all_players_query: Query<(Entity, &Player)>,
|
||||||
|
connection: Res<Connection>,
|
||||||
|
mut next_scene: ResMut<NextState<CurrentScene>>,
|
||||||
|
) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
crates/border-wars/src/networking/mod.rs
Normal file
32
crates/border-wars/src/networking/mod.rs
Normal file
|
@ -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,
|
||||||
|
|
||||||
CoCo_Sol marked this conversation as resolved
Outdated
raphael
commented
Es ce que l'admin peut aussi jouer ? Es ce que l'admin peut aussi jouer ?
|
|||||||
|
/// The player. He can join the game and play.
|
||||||
|
Player,
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
//! The lobby of the game.
|
//! The lobby of the game.
|
||||||
|
|
||||||
|
use bevnet::{Connection, SendTo};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_egui::{egui, EguiContexts};
|
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.
|
/// The plugin for the lobby.
|
||||||
pub struct LobbyPlugin;
|
pub struct LobbyPlugin;
|
||||||
|
@ -15,7 +18,21 @@ impl Plugin for LobbyPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Display the UI of the lobby.
|
/// Display the UI of the lobby.
|
||||||
fn lobby_ui(mut ctx: EguiContexts, mut next_scene: ResMut<NextState<CurrentScene>>) {
|
fn lobby_ui(
|
||||||
|
mut ctx: EguiContexts,
|
||||||
|
mut next_scene: ResMut<NextState<CurrentScene>>,
|
||||||
|
connection: Res<Connection>,
|
||||||
|
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| {
|
egui::CentralPanel::default().show(ctx.ctx_mut(), |ui| {
|
||||||
ui.heading("Border Wars");
|
ui.heading("Border Wars");
|
||||||
|
|
||||||
|
@ -23,14 +40,30 @@ fn lobby_ui(mut ctx: EguiContexts, mut next_scene: ResMut<NextState<CurrentScene
|
||||||
|
|
||||||
ui.label("Game created");
|
ui.label("Game created");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
if self_player.rank != PlayerRank::Admin {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ui.label("Game ID: ");
|
ui.label("Game ID: ");
|
||||||
// TODO : get the game ID and display it.
|
// TODO : get the game ID and display it.
|
||||||
ui.label("connection_string");
|
ui.text_edit_singleline(&mut connection.identifier().unwrap_or_default().to_string());
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
if ui.button("Run the game").clicked() {
|
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_query.iter() {
|
||||||
|
kick_player.send(SendTo(sender_id.uuid, RemovePlayer(player.clone())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self_player.rank == PlayerRank::Admin && ui.button("Run the game").clicked() {
|
||||||
next_scene.set(CurrentScene::Game);
|
next_scene.set(CurrentScene::Game);
|
||||||
// TODO: run the game
|
// TODO: run the game
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
//! The main menu of the game.
|
//! The main menu of the game.
|
||||||
|
|
||||||
|
use bevnet::{Connection, SendTo, Uuid};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_egui::{egui, EguiContexts};
|
use bevy_egui::{egui, EguiContexts};
|
||||||
|
|
||||||
use crate::CurrentScene;
|
use crate::networking::connection::RequestJoin;
|
||||||
|
use crate::networking::PlayerRank;
|
||||||
|
use crate::{CurrentScene, Player};
|
||||||
|
|
||||||
/// The plugin for the menu.
|
/// The plugin for the menu.
|
||||||
pub struct MenuPlugin;
|
pub struct MenuPlugin;
|
||||||
|
@ -13,25 +16,50 @@ impl Plugin for MenuPlugin {
|
||||||
app.add_systems(Update, menu_ui.run_if(in_state(CurrentScene::Menu)));
|
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.
|
/// Display the UI of the menu to host a game or join one.
|
||||||
fn menu_ui(
|
fn menu_ui(
|
||||||
mut ctx: EguiContexts,
|
mut ctx: EguiContexts,
|
||||||
mut connection_string: Local<String>,
|
mut connection_string: Local<String>,
|
||||||
mut next_scene: ResMut<NextState<CurrentScene>>,
|
mut next_scene: ResMut<NextState<CurrentScene>>,
|
||||||
|
mut request_join: EventWriter<SendTo<RequestJoin>>,
|
||||||
|
mut name: Local<String>,
|
||||||
|
connection: Res<Connection>,
|
||||||
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
|
let Some(uuid) = connection.identifier() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx.ctx_mut(), |ui| {
|
egui::CentralPanel::default().show(ctx.ctx_mut(), |ui| {
|
||||||
ui.heading("Border Wars");
|
ui.heading("Border Wars");
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
ui.label("Name");
|
||||||
|
ui.text_edit_singleline(&mut *name);
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
ui.label("Connect to an existing game:");
|
ui.label("Connect to an existing game:");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Game ID: ");
|
ui.label("Game ID: ");
|
||||||
ui.text_edit_singleline(&mut *connection_string);
|
ui.text_edit_singleline(&mut *connection_string);
|
||||||
|
|
||||||
|
let Ok(game_id) = Uuid::parse_str(&connection_string) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if ui.button("Join").clicked() {
|
if ui.button("Join").clicked() {
|
||||||
next_scene.set(CurrentScene::Game);
|
next_scene.set(CurrentScene::Lobby);
|
||||||
// TODO: connect to the game
|
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() {
|
if ui.button("Create new game").clicked() {
|
||||||
next_scene.set(CurrentScene::Lobby);
|
next_scene.set(CurrentScene::Lobby);
|
||||||
// TODO: create a new game
|
commands.spawn(Player {
|
||||||
|
name: name.clone(),
|
||||||
|
rank: PlayerRank::Admin,
|
||||||
|
uuid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl Connection {
|
||||||
path.push(".relay-data");
|
path.push(".relay-data");
|
||||||
|
|
||||||
// Check if the file exists.
|
// Check if the file exists.
|
||||||
match path.exists() {
|
match false {
|
||||||
true => {
|
true => {
|
||||||
// Read the file and parse the identifier and secret key.
|
// Read the file and parse the identifier and secret key.
|
||||||
let contents = fs::read(&path)?;
|
let contents = fs::read(&path)?;
|
||||||
|
|
Loading…
Reference in a new issue
Pas compris pk il doit être admin ni qui est le joueur en question