From 9e48af01ae56fe813869ada24de291ab98b361e2 Mon Sep 17 00:00:00 2001 From: "1992414357@qq.com" <1992414357@qq.com> Date: Wed, 11 Jun 2025 19:14:04 +0800 Subject: 组合模块中 structs.rs 和 implements.rs 文件 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/Cargo.toml | 6 +- core/examples/connect.rs | 3 +- core/examples/tcp_server.rs | 2 +- core/src/data/controller/cli/cli_command.rs | 130 ------- core/src/data/controller/cli/mod.rs | 1 - core/src/data/controller/controller_cli.rs | 130 +++++++ core/src/data/controller/controller_data.rs | 29 ++ core/src/data/controller/controller_runtime.rs | 75 ++++ core/src/data/controller/implements.rs | 21 -- core/src/data/controller/mod.rs | 7 +- core/src/data/controller/runtime/implements.rs | 58 --- core/src/data/controller/runtime/mod.rs | 2 - core/src/data/controller/runtime/structs.rs | 21 -- core/src/data/controller/structs.rs | 10 - core/src/data/game/cli/cli_command.rs | 192 ---------- core/src/data/game/cli/mod.rs | 1 - core/src/data/game/game_cli.rs | 192 ++++++++++ core/src/data/game/game_data.rs | 125 +++++++ core/src/data/game/game_runtime.rs | 411 +++++++++++++++++++++ core/src/data/game/implements.rs | 99 ----- core/src/data/game/mod.rs | 7 +- core/src/data/game/runtime/implements.rs | 376 ------------------- core/src/data/game/runtime/mod.rs | 2 - core/src/data/game/runtime/structs.rs | 42 --- core/src/data/game/structs.rs | 29 -- core/src/data/game/types.rs | 2 +- core/src/data/message/enums.rs | 154 -------- core/src/data/message/implements.rs | 15 - core/src/data/message/message_encoders.rs | 15 + core/src/data/message/message_enums.rs | 154 ++++++++ core/src/data/message/mod.rs | 4 +- core/src/data/player/implements.rs | 119 ------ core/src/data/player/mod.rs | 3 +- core/src/data/player/player_data.rs | 162 ++++++++ core/src/data/player/structs.rs | 45 --- core/src/lib.rs | 2 +- core/src/message_encoders.rs | 8 + core/src/service/tcp_network/long_connection.rs | 12 +- .../service/tcp_network/pad_client/implements.rs | 161 -------- core/src/service/tcp_network/pad_client/mod.rs | 3 +- .../tcp_network/pad_client/pad_client_service.rs | 165 +++++++++ core/src/service/tcp_network/pad_client/structs.rs | 8 - .../service/tcp_network/pad_server/implements.rs | 186 ---------- core/src/service/tcp_network/pad_server/mod.rs | 3 +- .../tcp_network/pad_server/pad_server_service.rs | 193 ++++++++++ core/src/service/tcp_network/pad_server/structs.rs | 12 - 46 files changed, 1681 insertions(+), 1716 deletions(-) delete mode 100644 core/src/data/controller/cli/cli_command.rs delete mode 100644 core/src/data/controller/cli/mod.rs create mode 100644 core/src/data/controller/controller_cli.rs create mode 100644 core/src/data/controller/controller_data.rs create mode 100644 core/src/data/controller/controller_runtime.rs delete mode 100644 core/src/data/controller/implements.rs delete mode 100644 core/src/data/controller/runtime/implements.rs delete mode 100644 core/src/data/controller/runtime/mod.rs delete mode 100644 core/src/data/controller/runtime/structs.rs delete mode 100644 core/src/data/controller/structs.rs delete mode 100644 core/src/data/game/cli/cli_command.rs delete mode 100644 core/src/data/game/cli/mod.rs create mode 100644 core/src/data/game/game_cli.rs create mode 100644 core/src/data/game/game_data.rs create mode 100644 core/src/data/game/game_runtime.rs delete mode 100644 core/src/data/game/implements.rs delete mode 100644 core/src/data/game/runtime/implements.rs delete mode 100644 core/src/data/game/runtime/mod.rs delete mode 100644 core/src/data/game/runtime/structs.rs delete mode 100644 core/src/data/game/structs.rs delete mode 100644 core/src/data/message/enums.rs delete mode 100644 core/src/data/message/implements.rs create mode 100644 core/src/data/message/message_encoders.rs create mode 100644 core/src/data/message/message_enums.rs delete mode 100644 core/src/data/player/implements.rs create mode 100644 core/src/data/player/player_data.rs delete mode 100644 core/src/data/player/structs.rs create mode 100644 core/src/message_encoders.rs delete mode 100644 core/src/service/tcp_network/pad_client/implements.rs create mode 100644 core/src/service/tcp_network/pad_client/pad_client_service.rs delete mode 100644 core/src/service/tcp_network/pad_client/structs.rs delete mode 100644 core/src/service/tcp_network/pad_server/implements.rs create mode 100644 core/src/service/tcp_network/pad_server/pad_server_service.rs delete mode 100644 core/src/service/tcp_network/pad_server/structs.rs (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 6758217..9b9d42c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,8 +13,4 @@ serde = { version = "1.0.219", features = ["derive"] } clap = { version = "4.5.38", features = ["derive"] } sha1 = "0.10.6" log = "0.4.27" -shell-words = "1.1.0" - -[lib] -name = "nogamepads_core" -crate-type = [ "rlib"] \ No newline at end of file +shell-words = "1.1.0" \ No newline at end of file diff --git a/core/examples/connect.rs b/core/examples/connect.rs index 1fd9bca..e6f70c4 100644 --- a/core/examples/connect.rs +++ b/core/examples/connect.rs @@ -22,7 +22,6 @@ fn main () { let runtime = controller.runtime(); let client = PadClientNetwork::build(Arc::clone(&runtime)); - // client.connect(); - // or + build_tokio_runtime("Tokio Runtime".to_string()).block_on(client.build_entry()) } \ No newline at end of file diff --git a/core/examples/tcp_server.rs b/core/examples/tcp_server.rs index 9dce538..87e63c8 100644 --- a/core/examples/tcp_server.rs +++ b/core/examples/tcp_server.rs @@ -1,6 +1,6 @@ use clap::CommandFactory; use nogamepads::logger_utils::logger_build; -use nogamepads_core::data::game::cli::cli_command::{process_game_cli, GameCli}; +use nogamepads_core::data::game::game_cli::{process_game_cli, GameCli}; use nogamepads_core::data::game::structs::{GameData, GameRuntimeDataArchive}; use nogamepads_core::run_services; use nogamepads_core::service::cli_addition::runtime_consoles::RuntimeConsole; diff --git a/core/src/data/controller/cli/cli_command.rs b/core/src/data/controller/cli/cli_command.rs deleted file mode 100644 index 9fee0b0..0000000 --- a/core/src/data/controller/cli/cli_command.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::sync::{Arc, Mutex}; -use clap::{Args, Parser, Subcommand}; -use clearscreen::clear; -use log::info; -use nogamepads::entry_mutex; -use crate::data::controller::runtime::structs::ControllerRuntime; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -pub struct ControllerCli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand, Debug)] -enum Commands { - - #[command(about = "Clean the screen")] - Clear, - - #[command(about = "Close the controller")] - Close, - - #[command(about = "Send a message")] - Message(MessageArgs), - - #[command(about = "Press a button")] - Press(ButtonArgs), - - #[command(about = "Release a button")] - Release(ButtonArgs), - - #[command(about = "Change a axis value")] - Axis(AxisArgs), - - #[command(about = "Change a direction value")] - Direction(DirectionArgs), - - Pop, - - PopAll -} - -#[derive(Args, Debug)] -struct MessageArgs { - message: String, -} - -#[derive(Args, Debug)] -struct ButtonArgs { - button_key: u8 -} - -#[derive(Args, Debug)] -struct AxisArgs { - axis_key: u8, - axis_value: f64 -} - -#[derive(Args, Debug)] -struct DirectionArgs { - dir_key: u8, - x: f64, - y: f64 -} - -pub fn process_controller_cli(runtime: Arc>, cmd: ControllerCli) -> bool { - - match cmd.command { - Commands::Clear => { - clear().expect("Failed to clear screen"); - } - - Commands::Close => { - entry_mutex!(runtime, |guard| { - guard.close(); - }); - return false; - } - - Commands::Message(args) => { - entry_mutex!(runtime, |guard| { - guard.message(args.message); - }); - } - - Commands::Press(args) => { - entry_mutex!(runtime, |guard| { - guard.press_button(args.button_key); - }); - } - - Commands::Release(args) => { - entry_mutex!(runtime, |guard| { - guard.release_button(args.button_key); - }); - } - - Commands::Axis(args) => { - entry_mutex!(runtime, |guard| { - guard.change_axis(args.axis_key, args.axis_value); - }); - } - - Commands::Direction(args) => { - entry_mutex!(runtime, |guard| { - guard.change_direction(args.dir_key, args.x, args.y); - }); - } - - Commands::Pop => { - entry_mutex!(runtime, |guard| { - if let Some(msg) = guard.pop() { - info!("{:?}", msg); - } else { - info!("None"); - } - }); - } - - Commands::PopAll => { - entry_mutex!(runtime, |guard| { - while let Some(msg) = guard.pop() { - info!("{:?}", msg); - } - }); - } - } - true -} \ No newline at end of file diff --git a/core/src/data/controller/cli/mod.rs b/core/src/data/controller/cli/mod.rs deleted file mode 100644 index 043f7b8..0000000 --- a/core/src/data/controller/cli/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod cli_command; \ No newline at end of file diff --git a/core/src/data/controller/controller_cli.rs b/core/src/data/controller/controller_cli.rs new file mode 100644 index 0000000..9ab49d6 --- /dev/null +++ b/core/src/data/controller/controller_cli.rs @@ -0,0 +1,130 @@ +use std::sync::{Arc, Mutex}; +use clap::{Args, Parser, Subcommand}; +use clearscreen::clear; +use log::info; +use nogamepads::entry_mutex; +use crate::data::controller::controller_runtime::ControllerRuntime; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct ControllerCli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + + #[command(about = "Clean the screen")] + Clear, + + #[command(about = "Close the controller")] + Close, + + #[command(about = "Send a message")] + Message(MessageArgs), + + #[command(about = "Press a button")] + Press(ButtonArgs), + + #[command(about = "Release a button")] + Release(ButtonArgs), + + #[command(about = "Change a axis value")] + Axis(AxisArgs), + + #[command(about = "Change a direction value")] + Direction(DirectionArgs), + + Pop, + + PopAll +} + +#[derive(Args, Debug)] +struct MessageArgs { + message: String, +} + +#[derive(Args, Debug)] +struct ButtonArgs { + button_key: u8 +} + +#[derive(Args, Debug)] +struct AxisArgs { + axis_key: u8, + axis_value: f64 +} + +#[derive(Args, Debug)] +struct DirectionArgs { + dir_key: u8, + x: f64, + y: f64 +} + +pub fn process_controller_cli(runtime: Arc>, cmd: ControllerCli) -> bool { + + match cmd.command { + Commands::Clear => { + clear().expect("Failed to clear screen"); + } + + Commands::Close => { + entry_mutex!(runtime, |guard| { + guard.close(); + }); + return false; + } + + Commands::Message(args) => { + entry_mutex!(runtime, |guard| { + guard.message(args.message); + }); + } + + Commands::Press(args) => { + entry_mutex!(runtime, |guard| { + guard.press_button(args.button_key); + }); + } + + Commands::Release(args) => { + entry_mutex!(runtime, |guard| { + guard.release_button(args.button_key); + }); + } + + Commands::Axis(args) => { + entry_mutex!(runtime, |guard| { + guard.change_axis(args.axis_key, args.axis_value); + }); + } + + Commands::Direction(args) => { + entry_mutex!(runtime, |guard| { + guard.change_direction(args.dir_key, args.x, args.y); + }); + } + + Commands::Pop => { + entry_mutex!(runtime, |guard| { + if let Some(msg) = guard.pop() { + info!("{:?}", msg); + } else { + info!("None"); + } + }); + } + + Commands::PopAll => { + entry_mutex!(runtime, |guard| { + while let Some(msg) = guard.pop() { + info!("{:?}", msg); + } + }); + } + } + true +} \ No newline at end of file diff --git a/core/src/data/controller/controller_data.rs b/core/src/data/controller/controller_data.rs new file mode 100644 index 0000000..a3f1c02 --- /dev/null +++ b/core/src/data/controller/controller_data.rs @@ -0,0 +1,29 @@ +use std::sync::{Arc, Mutex}; +use crate::data::controller::controller_runtime::ControllerRuntime; +use crate::data::player::player_data::Player; + +/// Controller-side Data +/// Describes the basic information of the controller side +#[derive(Default)] +pub struct ControllerData { + + /// Player bound to the controller side + pub(crate) player: Player +} + +impl ControllerData { + + pub fn bind_player(&mut self, player: Player) -> &mut ControllerData { + self.player = player; + self + } + + /// Build the controller-side runtime using controller data + pub fn runtime(self) -> Arc> { + let runtime = ControllerRuntime { + player: self.player, + ..Default::default() + }; + Arc::new(Mutex::new(runtime)) + } +} \ No newline at end of file diff --git a/core/src/data/controller/controller_runtime.rs b/core/src/data/controller/controller_runtime.rs new file mode 100644 index 0000000..5e7bb6d --- /dev/null +++ b/core/src/data/controller/controller_runtime.rs @@ -0,0 +1,75 @@ +use std::collections::{HashMap, VecDeque}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::SeqCst; +use log::trace; +use crate::data::game::types::GameInfo; +use crate::data::message::message_enums::{ControlMessage, GameMessage}; +use crate::data::message::traits::MessageManager; +use crate::data::player::player_data::Player; +use crate::service::service_types::ServiceType; + +/// Controller-side runtime +/// Stores all data involved in game pad_client interactions during runtime +#[derive(Default)] +pub struct ControllerRuntime { + + pub(crate) service_type: ServiceType, + pub(crate) received: HashMap<(ServiceType, u8), VecDeque>, + pub(crate) send: HashMap<(ServiceType, u8), VecDeque>, + + pub(crate) player: Player, + + pub game_info: GameInfo, + pub close: AtomicBool, +} + +/// Message manager for controller-side runtime +/// After the service starts, it can be accessed or relevant messages can be stored. +impl MessageManager for ControllerRuntime { + fn borrow_received_list_mut(&mut self) -> &mut HashMap<(ServiceType, u8), VecDeque> { + &mut self.received + } + + fn borrow_send_list_mut(&mut self) -> &mut HashMap<(ServiceType, u8), VecDeque> { + &mut self.send + } +} + +impl ControllerRuntime { + + pub fn close(&mut self) { + if !self.close.load(SeqCst) { + self.close.store(true, SeqCst); + trace!("[Controller Runtime] Closed."); + } + } + + pub fn message(&mut self, message: String) { + self.send_message(ControlMessage::Msg(message)); + } + + pub fn press_button(&mut self, key: u8) { + self.send_message(ControlMessage::Pressed(key)); + } + + pub fn release_button(&mut self, key: u8) { + self.send_message(ControlMessage::Released(key)); + } + + pub fn change_axis(&mut self, key: u8, ax_val: f64) { + self.send_message(ControlMessage::Axis(key, ax_val)); + } + + pub fn change_direction(&mut self, key: u8, x: f64, y: f64) { + self.send_message(ControlMessage::Dir(key, (x, y))); + } + + pub fn pop(&mut self) -> Option { + self.receive(0, self.service_type.clone()) + } + + fn send_message (&mut self, msg: ControlMessage) { + let service = self.service_type.clone(); + self.send(msg, 0, service); + } +} \ No newline at end of file diff --git a/core/src/data/controller/implements.rs b/core/src/data/controller/implements.rs deleted file mode 100644 index 86a19de..0000000 --- a/core/src/data/controller/implements.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::sync::{Arc, Mutex}; -use crate::data::controller::runtime::structs::ControllerRuntime; -use crate::data::controller::structs::ControllerData; -use crate::data::player::structs::Player; - -impl ControllerData { - - pub fn bind_player(&mut self, player: Player) -> &mut ControllerData { - self.player = player; - self - } - - /// Build the controller-side runtime using controller data - pub fn runtime(self) -> Arc> { - let runtime = ControllerRuntime { - player: self.player, - ..Default::default() - }; - Arc::new(Mutex::new(runtime)) - } -} \ No newline at end of file diff --git a/core/src/data/controller/mod.rs b/core/src/data/controller/mod.rs index 019ebcf..ce9d9c3 100644 --- a/core/src/data/controller/mod.rs +++ b/core/src/data/controller/mod.rs @@ -1,4 +1,3 @@ -pub mod cli; -pub mod runtime; -pub mod implements; -pub mod structs; \ No newline at end of file +pub mod controller_cli; +pub mod controller_runtime; +pub mod controller_data; \ No newline at end of file diff --git a/core/src/data/controller/runtime/implements.rs b/core/src/data/controller/runtime/implements.rs deleted file mode 100644 index 1f2734a..0000000 --- a/core/src/data/controller/runtime/implements.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::collections::{HashMap, VecDeque}; -use std::sync::atomic::Ordering::SeqCst; -use log::trace; -use crate::data::controller::runtime::structs::ControllerRuntime; -use crate::data::message::enums::{ControlMessage, GameMessage}; -use crate::data::message::traits::MessageManager; -use crate::service::service_types::ServiceType; - -/// Message manager for controller-side runtime -/// After the service starts, it can be accessed or relevant messages can be stored. -impl MessageManager for ControllerRuntime { - fn borrow_received_list_mut(&mut self) -> &mut HashMap<(ServiceType, u8), VecDeque> { - &mut self.received - } - - fn borrow_send_list_mut(&mut self) -> &mut HashMap<(ServiceType, u8), VecDeque> { - &mut self.send - } -} - -impl ControllerRuntime { - - pub fn close(&mut self) { - if !self.close.load(SeqCst) { - self.close.store(true, SeqCst); - trace!("[Controller Runtime] Closed."); - } - } - - pub fn message(&mut self, message: String) { - self.send_message(ControlMessage::Msg(message)); - } - - pub fn press_button(&mut self, key: u8) { - self.send_message(ControlMessage::Pressed(key)); - } - - pub fn release_button(&mut self, key: u8) { - self.send_message(ControlMessage::Released(key)); - } - - pub fn change_axis(&mut self, key: u8, ax_val: f64) { - self.send_message(ControlMessage::Axis(key, ax_val)); - } - - pub fn change_direction(&mut self, key: u8, x: f64, y: f64) { - self.send_message(ControlMessage::Dir(key, (x, y))); - } - - pub fn pop(&mut self) -> Option { - self.receive(0, self.service_type.clone()) - } - - fn send_message (&mut self, msg: ControlMessage) { - let service = self.service_type.clone(); - self.send(msg, 0, service); - } -} \ No newline at end of file diff --git a/core/src/data/controller/runtime/mod.rs b/core/src/data/controller/runtime/mod.rs deleted file mode 100644 index 0ff870f..0000000 --- a/core/src/data/controller/runtime/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod implements; -pub mod structs; \ No newline at end of file diff --git a/core/src/data/controller/runtime/structs.rs b/core/src/data/controller/runtime/structs.rs deleted file mode 100644 index bfd39c5..0000000 --- a/core/src/data/controller/runtime/structs.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::{HashMap, VecDeque}; -use std::sync::atomic::AtomicBool; -use crate::data::game::types::GameInfo; -use crate::data::message::enums::{ControlMessage, GameMessage}; -use crate::data::player::structs::Player; -use crate::service::service_types::ServiceType; - -/// Controller-side runtime -/// Stores all data involved in game pad_client interactions during runtime -#[derive(Default)] -pub struct ControllerRuntime { - - pub(crate) service_type: ServiceType, - pub(crate) received: HashMap<(ServiceType, u8), VecDeque>, - pub(crate) send: HashMap<(ServiceType, u8), VecDeque>, - - pub(crate) player: Player, - - pub game_info: GameInfo, - pub close: AtomicBool, -} \ No newline at end of file diff --git a/core/src/data/controller/structs.rs b/core/src/data/controller/structs.rs deleted file mode 100644 index c6fdd31..0000000 --- a/core/src/data/controller/structs.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::data::player::structs::Player; - -/// Controller-side Data -/// Describes the basic information of the controller side -#[derive(Default)] -pub struct ControllerData { - - /// Player bound to the controller side - pub(crate) player: Player -} \ No newline at end of file diff --git a/core/src/data/game/cli/cli_command.rs b/core/src/data/game/cli/cli_command.rs deleted file mode 100644 index 5f296f8..0000000 --- a/core/src/data/game/cli/cli_command.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::sync::{Arc, Mutex}; -use clap::{Args, Parser, Subcommand}; -use clearscreen::clear; -use log::{info, warn}; -use nogamepads::entry_mutex; -use crate::data::game::runtime::structs::GameRuntime; -use crate::data::player::structs::Player; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -pub struct GameCli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand, Debug)] -enum Commands { - - #[command(about = "Clean the screen")] - Clear, - - LockGame, - - UnlockGame, - - #[command(about = "Close the game")] - Close, - - OnlineList, - - BannedList, - - Ban(PlayerIndex), - - Pardon(PlayerIndex), - - Kick(PlayerIndex), - - Event(SendEventArgs), - - Message(SendMessageArgs), - - Pop, - - PopAll, -} - -#[derive(Args, Debug)] -struct PlayerIndex { - index: usize, -} - -#[derive(Args, Debug)] -struct SendEventArgs { - index: usize, - event: u8 -} - -#[derive(Args, Debug)] -struct SendMessageArgs { - index: usize, - msg: String -} - -pub fn process_game_cli(runtime: Arc>, cmd: GameCli) -> bool { - match cmd.command { - Commands::Clear => { - clear().expect("Failed to clear screen"); - } - - Commands::LockGame => { - entry_mutex!(runtime, |guard| { - guard.lock_game(); - }); - } - - Commands::UnlockGame => { - entry_mutex!(runtime, |guard| { - guard.unlock_game(); - }); - } - - Commands::Close => { - entry_mutex!(runtime, |guard| { - guard.close_game(); - }); - return false; - } - - Commands::OnlineList => { - entry_mutex!(runtime, |guard| { - let mut i = 0; - for account in guard.data.online_accounts() { - info!("{}.{}", i, account.id); - i += 1; - } - }); - } - - Commands::BannedList => { - entry_mutex!(runtime, |guard| { - let mut i = 0; - for account in guard.data.banned_accounts() { - info!("{}.{}", i, account.id); - i += 1; - } - }); - } - - Commands::Ban(args) => { - entry_mutex!(runtime, |guard| { - if let Some(account) = guard.data.online_accounts().get(args.index) { - if let Some(service_type) = guard.data.get_service_type(account) { - guard.ban_player(&Player::from(account.clone()), service_type); - info!("Account {} banned.", account.id); - } - } else { - warn!("Account number {} not found", args.index); - } - }); - } - - Commands::Pardon(args) => { - entry_mutex!(runtime, |guard| { - if let Some(account) = guard.data.banned_accounts().get(args.index) { - guard.pardon_player(&Player::from(account.clone())); - info!("Account {} pardoned.", account.id); - } else { - warn!("Account number {} not found", args.index); - } - }); - } - - Commands::Kick(args) => { - entry_mutex!(runtime, |guard| { - if let Some(account) = guard.data.online_accounts().get(args.index) { - if let Some(service_type) = guard.data.get_service_type(account) { - guard.kick_player(&Player::from(account.clone()), service_type); - info!("Account {} kicked.", account.id); - } - } else { - warn!("Account number {} not found", args.index); - } - }); - } - - Commands::Event(args) => { - entry_mutex!(runtime, |guard| { - if let Some(account) = guard.data.online_accounts().get(args.index) { - if let Some(service_type) = guard.data.get_service_type(account) { - guard.send_event(account, args.event, service_type); - info!("Sent event {} to {}.", args.event, account.id); - } - } else { - warn!("Account number {} not found", args.index); - } - }); - } - - Commands::Message(args) => { - entry_mutex!(runtime, |guard| { - if let Some(account) = guard.data.online_accounts().get(args.index) { - if let Some(service_type) = guard.data.get_service_type(account) { - guard.send_message(account, args.msg.clone(), service_type); - info!("Sent message \"{}\" to {}.", args.msg, account.id); - } - } else { - warn!("Account number {} not found", args.index); - } - }); - } - - Commands::Pop => { - entry_mutex!(runtime, |guard| { - if let Some((account, message)) = guard.pop_event() { - info!("{}: {:?}", account.id, message); - } else { - info!("None") - } - }); - } - - Commands::PopAll => { - entry_mutex!(runtime, |guard| { - while let Some((account, message)) = guard.pop_event() { - info!("{}: {:?}", account.id, message); - } - }); - } - } - true -} \ No newline at end of file diff --git a/core/src/data/game/cli/mod.rs b/core/src/data/game/cli/mod.rs deleted file mode 100644 index 043f7b8..0000000 --- a/core/src/data/game/cli/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod cli_command; \ No newline at end of file diff --git a/core/src/data/game/game_cli.rs b/core/src/data/game/game_cli.rs new file mode 100644 index 0000000..521de3f --- /dev/null +++ b/core/src/data/game/game_cli.rs @@ -0,0 +1,192 @@ +use std::sync::{Arc, Mutex}; +use clap::{Args, Parser, Subcommand}; +use clearscreen::clear; +use log::{info, warn}; +use nogamepads::entry_mutex; +use crate::data::game::game_runtime::GameRuntime; +use crate::data::player::player_data::Player; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct GameCli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + + #[command(about = "Clean the screen")] + Clear, + + LockGame, + + UnlockGame, + + #[command(about = "Close the game")] + Close, + + OnlineList, + + BannedList, + + Ban(PlayerIndex), + + Pardon(PlayerIndex), + + Kick(PlayerIndex), + + Event(SendEventArgs), + + Message(SendMessageArgs), + + Pop, + + PopAll, +} + +#[derive(Args, Debug)] +struct PlayerIndex { + index: usize, +} + +#[derive(Args, Debug)] +struct SendEventArgs { + index: usize, + event: u8 +} + +#[derive(Args, Debug)] +struct SendMessageArgs { + index: usize, + msg: String +} + +pub fn process_game_cli(runtime: Arc>, cmd: GameCli) -> bool { + match cmd.command { + Commands::Clear => { + clear().expect("Failed to clear screen"); + } + + Commands::LockGame => { + entry_mutex!(runtime, |guard| { + guard.lock_game(); + }); + } + + Commands::UnlockGame => { + entry_mutex!(runtime, |guard| { + guard.unlock_game(); + }); + } + + Commands::Close => { + entry_mutex!(runtime, |guard| { + guard.close_game(); + }); + return false; + } + + Commands::OnlineList => { + entry_mutex!(runtime, |guard| { + let mut i = 0; + for account in guard.data.online_accounts() { + info!("{}.{}", i, account.id); + i += 1; + } + }); + } + + Commands::BannedList => { + entry_mutex!(runtime, |guard| { + let mut i = 0; + for account in guard.data.banned_accounts() { + info!("{}.{}", i, account.id); + i += 1; + } + }); + } + + Commands::Ban(args) => { + entry_mutex!(runtime, |guard| { + if let Some(account) = guard.data.online_accounts().get(args.index) { + if let Some(service_type) = guard.data.get_service_type(account) { + guard.ban_player(&Player::from(account.clone()), service_type); + info!("Account {} banned.", account.id); + } + } else { + warn!("Account number {} not found", args.index); + } + }); + } + + Commands::Pardon(args) => { + entry_mutex!(runtime, |guard| { + if let Some(account) = guard.data.banned_accounts().get(args.index) { + guard.pardon_player(&Player::from(account.clone())); + info!("Account {} pardoned.", account.id); + } else { + warn!("Account number {} not found", args.index); + } + }); + } + + Commands::Kick(args) => { + entry_mutex!(runtime, |guard| { + if let Some(account) = guard.data.online_accounts().get(args.index) { + if let Some(service_type) = guard.data.get_service_type(account) { + guard.kick_player(&Player::from(account.clone()), service_type); + info!("Account {} kicked.", account.id); + } + } else { + warn!("Account number {} not found", args.index); + } + }); + } + + Commands::Event(args) => { + entry_mutex!(runtime, |guard| { + if let Some(account) = guard.data.online_accounts().get(args.index) { + if let Some(service_type) = guard.data.get_service_type(account) { + guard.send_event(account, args.event, service_type); + info!("Sent event {} to {}.", args.event, account.id); + } + } else { + warn!("Account number {} not found", args.index); + } + }); + } + + Commands::Message(args) => { + entry_mutex!(runtime, |guard| { + if let Some(account) = guard.data.online_accounts().get(args.index) { + if let Some(service_type) = guard.data.get_service_type(account) { + guard.send_message(account, args.msg.clone(), service_type); + info!("Sent message \"{}\" to {}.", args.msg, account.id); + } + } else { + warn!("Account number {} not found", args.index); + } + }); + } + + Commands::Pop => { + entry_mutex!(runtime, |guard| { + if let Some((account, message)) = guard.pop_event() { + info!("{}: {:?}", account.id, message); + } else { + info!("None") + } + }); + } + + Commands::PopAll => { + entry_mutex!(runtime, |guard| { + while let Some((account, message)) = guard.pop_event() { + info!("{}: {:?}", account.id, message); + } + }); + } + } + true +} \ No newline at end of file diff --git a/core/src/data/game/game_data.rs b/core/src/data/game/game_data.rs new file mode 100644 index 0000000..3bdf28b --- /dev/null +++ b/core/src/data/game/game_data.rs @@ -0,0 +1,125 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use serde::{Deserialize, Serialize}; +use nogamepads::entry_mutex; +use crate::data::game::game_runtime::{GameControlRuntime, GameRuntime, GameRuntimeData}; +use crate::data::game::types::{GameInfo, Players}; +use crate::data::player::player_data::{Account, Player}; + +/// Game pad_client data +/// Describes the basic information of the game pad_client +#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +pub struct GameData { + pub info: GameInfo, + pub control: GameControlData, + pub archive: GameRuntimeDataArchive +} + +/// Game control information +/// Describes the buttons, axes, and directions that can be controlled. +#[derive(Default, Clone, Serialize, Deserialize, PartialEq, Debug)] +pub struct GameControlData { + pub direction_keys : HashMap, + pub axis_keys : HashMap, + pub button_keys : HashMap, +} + +/// Archive of game runtime data +/// The game pad_client can convert data into this structure for persistence. +#[derive(Default, Clone, Serialize, Deserialize, PartialEq, Debug)] +pub struct GameRuntimeDataArchive { + pub banned: Vec +} + +impl Default for GameData { + fn default() -> Self { + GameData::new() + } +} + +impl GameData { + + /// Create new game data + pub fn new() -> GameData { + let mut game = GameData { + info: GameInfo::default(), + control: GameControlData::default(), + archive: GameRuntimeDataArchive::default(), + }; + + game.name("Mini Hero".to_string()); + game.version(env!("PROJECT_VERSION").to_string()); + game + } + + /// Add or modify game name information + pub fn name(&mut self, name: String) -> &mut GameData { + self.info("Game_Name".to_string(), name); + self + } + + /// Add or modify game version information + pub fn version(&mut self, version: String) -> &mut GameData { + self.info("Version".to_string(), version); + self + } + + /// Add or modify information for a specific entry + pub fn info(&mut self, name: String, value: String) -> &mut GameData { + self.info.insert(name, value); + self + } + + /// Read game runtime archive data + pub fn load_data(&mut self, storage: GameRuntimeDataArchive) -> &mut GameData { + self.archive = storage; + self + } + + /// Build the game-side runtime using game data + pub fn runtime(self) -> Arc> { + let runtime = GameRuntime { + info: self.info, + data: self.archive.into(), + control: GameControlRuntime { + keys: self.control, + ..Default::default() + }, + + writer_count: 0, + reader_count: 0, + }; + Arc::new(Mutex::new(runtime)) + } +} + +impl From for GameRuntimeData { + fn from(archive: GameRuntimeDataArchive) -> Self { + let banned_mutex = Players::default(); + entry_mutex!(banned_mutex, |guard| { + for account in archive.banned { + let player_info = Player::from(account.clone()); + guard.entry(account).or_insert_with(|| player_info); + } + }); + GameRuntimeData { + players_banned : banned_mutex, + ..Self::default() + } + } +} + +impl From for GameRuntimeDataArchive { + fn from(data: GameRuntimeData) -> Self { + let mut banned = Vec::new(); + entry_mutex!(data.players_online, |guard| { + for account in guard.keys().into_iter() { + banned.push(account.to_owned()); + } + }); + + GameRuntimeDataArchive { + banned + } + } +} \ No newline at end of file diff --git a/core/src/data/game/game_runtime.rs b/core/src/data/game/game_runtime.rs new file mode 100644 index 0000000..1c5a548 --- /dev/null +++ b/core/src/data/game/game_runtime.rs @@ -0,0 +1,411 @@ +use std::collections::{HashMap, VecDeque}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::Mutex; +use log::{info, trace, warn}; +use nogamepads::entry_mutex; +use crate::data::game::game_data::GameControlData; +use crate::data::game::types::{GameInfo, Players}; +use crate::data::message::message_enums::{JoinFailedMessage, ControlMessage, ExitReason, GameMessage}; +use crate::data::message::message_enums::JoinFailedMessage::{ContainIdenticalPlayer, GameLocked, PlayerBanned}; +use crate::data::message::message_enums::ControlMessage::{Axis, Dir, Msg, Pressed, Released}; +use crate::data::message::message_enums::ExitReason::{YouAreBanned, YouAreKicked}; +use crate::data::message::message_enums::GameMessage::{EventTrigger, LetExit}; +use crate::data::message::traits::MessageManager; +use crate::data::player::player_data::{Account, Player}; +use crate::service::service_types::ServiceType; +use crate::service::service_types::ServiceType::TCPConnection; + +/// Game pad_client runtime +/// Stores the game state, player information, and all data involved in controller-side interactions during runtime +pub struct GameRuntime { + + pub info: GameInfo, + pub data: GameRuntimeData, + pub control: GameControlRuntime, + + pub writer_count: i32, + pub reader_count: i32, +} + +pub struct GameRuntimeData { + + pub(crate) received: HashMap<(ServiceType, Account), VecDeque<(Account, ControlMessage)>>, + pub(crate) send: HashMap<(ServiceType, Account), VecDeque<(Account, GameMessage)>>, + + pub(crate) players_online: Players, + pub(crate) players_banned: Players, + pub(crate) account_service_type: Mutex>, + + pub locked: AtomicBool, + pub close: AtomicBool, +} + +#[derive(Default)] +pub struct GameControlRuntime { + pub(crate) keys: GameControlData, + pub(crate) directions : HashMap>, + pub(crate) axes : HashMap>, + pub(crate) button : HashMap>, + pub(crate) events : VecDeque<(Account, ControlMessage)> +} + +impl GameRuntime { + + /// Attempt to have the specified player join the game + pub fn try_join_player(&mut self, player: Player) -> Result<(), JoinFailedMessage> { + let join = self.can_join_game(&player.account); + match join { + Ok(_) => { + self.data.sign_player_online_status(&player, TCPConnection, true); + trace!("[Game Runtime] Player \"{}\" joined", player.account); + Ok(()) + } + Err(why) => { + warn!("[Game Runtime] Player \"{}\" join failed: {:?}", player.account, why); + Err(why) + } + } + } + + fn can_join_game(&self, account: &Account) -> Result { + + if self.is_game_locked() { + Err(GameLocked) + } else if self.data.is_account_banned(account) { + Err(PlayerBanned) + } else if self.data.is_account_online(account) { + Err(ContainIdenticalPlayer) + } else { + Ok(true) + } + } + + /// Request an account to exit + pub fn let_account_exit(&mut self, account: &Account, reason: ExitReason, service_type: ServiceType) { + // Send a leave message to the pad_client and wait for it to actively disconnect + if self.data.is_account_online(account) { + self.send((account.clone(), LetExit(reason)), account.clone(), service_type); + } + } + + pub fn kick_player(&mut self, player: &Player, service_type: ServiceType) { + // Send a leave message to the pad_client and wait for it to actively disconnect + if self.data.is_account_online(&player.account) { + self.send((player.account.clone(), LetExit(YouAreKicked)), player.account.clone(), service_type); + } + } + + pub fn ban_player(&mut self, player: &Player, service_type: ServiceType) { + if self.data.is_account_online(&player.account) { + self.send((player.account.clone(), LetExit(YouAreBanned)), player.account.clone(), service_type); + entry_mutex!(self.data.players_banned, |guard| { + guard.insert(player.account.clone(), player.clone()); + }); + } + } + + pub fn pardon_player(&mut self, player: &Player) { + entry_mutex!(self.data.players_banned, |guard| { + guard.remove(&player.account); + }); + } + + /// Check if the game is locked + pub fn is_game_locked(&self) -> bool { + self.data.locked.load(SeqCst) + } + + /// Lock the game + pub fn lock_game(&self) { + if !self.data.locked.load(SeqCst) { + self.data.locked.store(true, SeqCst); + info!("[Game Runtime] Game locked!"); + } + } + + /// Unlock the game + pub fn unlock_game(&self) { + if self.data.locked.load(SeqCst) { + self.data.locked.store(false, SeqCst); + info!("[Game Runtime] Game unlocked!"); + } + } + + /// Close the Game + pub fn close_game(&self) { + if !self.data.close.load(SeqCst) { + self.data.close.store(true, SeqCst); + info!("[Game Runtime] Game closed!"); + } + } + + /// Send a GameMessage to account + pub fn send_game_message(&mut self, account: &Account, message: GameMessage, service_type: ServiceType) { + self.send((account.clone(), message), account.clone(), service_type); + } + + pub fn send_event(&mut self, account: &Account, event_trigger: u8, service_type: ServiceType) { + self.send_game_message(account, EventTrigger(event_trigger), service_type); + } + + pub fn send_message(&mut self, account: &Account, message: String, service_type: ServiceType) { + self.send_game_message(account, GameMessage::Msg(message), service_type); + } + + /// Pop an event message + pub fn pop_event(&mut self) -> Option<(Account, ControlMessage)> { + let pop = self.control.events.pop_front(); + if pop.is_some() { + let (account, msg) = pop.unwrap(); + if self.data.is_account_online(&account) { + trace!("[Control Runtime] Message: {:?} from \"{}\" ", &msg, account); + Some((account, msg)) + } else { + warn!("[Control Runtime] Invalid message: Player \"{}\" is not online!", account); + None + } + } else { + None + } + } +} + +/// Message manager for game pad_client runtime +/// After the service starts, it can be accessed or relevant messages can be stored. +impl MessageManager<(Account, ControlMessage), (Account, GameMessage), Account> for GameRuntime { + fn borrow_received_list_mut(&mut self) -> &mut HashMap<(ServiceType, Account), VecDeque<(Account, ControlMessage)>> { + &mut self.data.received + } + + fn borrow_send_list_mut(&mut self) -> &mut HashMap<(ServiceType, Account), VecDeque<(Account, GameMessage)>> { + &mut self.data.send + } + + fn pop_from_send_list(&mut self, key: Account, service: ServiceType) -> Option<(Account, GameMessage)> { + let key = (service, key); + self.borrow_send_list_mut() + .entry(key) + .or_insert_with(VecDeque::new) + .pop_front() + } + + fn put_into_receive_list(&mut self, message: (Account, ControlMessage), _key: Account, _service: ServiceType) { + let result = self.control.process_control_message(&message.0, message.1); + if result.is_err() { + let result = result.unwrap_err(); + warn!("[Game Runtime] Can't process message: {:?}", result); + drop(result); + } + } +} + +impl Default for GameRuntimeData { + fn default() -> Self { + Self { + received: Default::default(), + send: Default::default(), + players_online: Players::default(), + players_banned: Players::default(), + account_service_type: Default::default(), + + locked: AtomicBool::new(false), + close: AtomicBool::new(false) + } + } +} + +impl GameRuntimeData { + + /// Mark a player as online + pub fn sign_player_online_status(&mut self, player: &Player, service_type: ServiceType, value: bool) { + let online = self.is_account_online(&player.account); + if online && !value { + + // Remove player + entry_mutex!(self.players_online, |guard| { + guard.remove_entry(&player.account); + }); + + info!("[Game Runtime] Signed player \"{}\" is [OFFLINE]!", player.account); + + // Reset runtime + let key = (service_type, player.account.clone()); + let get_received = self.received.get_mut(&key); + let get_send = self.send.get_mut(&key); + if let Some(list) = get_received { + list.clear(); + } + if let Some(list) = get_send { + list.clear(); + } + + } else if !online & value { + + // Insert player + entry_mutex!(self.players_online, |guard| { + guard.entry(player.account.clone()) + .or_insert_with(|| player.clone()); + }); + + info!("[Game Runtime] Signed player \"{}\" is [ONLINE]!", player.account); + + // Record service type + entry_mutex!(self.account_service_type, |guard| { + guard.entry(player.account.clone()) + .or_insert_with(|| TCPConnection); + }) + } + } + + /// Returns all online accounts + pub fn online_accounts(&self) -> Vec { + let mut vec = Vec::new(); + entry_mutex!(self.players_online, |guard| { + for account in guard.keys().into_iter() { + vec.push(account.clone()); + } + }); + vec + } + + /// Check if specified account is online + pub fn is_account_online(&self, account: &Account) -> bool { + entry_mutex!(self.players_online, |guard| { + if guard.contains_key(account) { + return true; + } + }); + false + } + + /// Returns all banned accounts + pub fn banned_accounts(&self) -> Vec { + let mut vec = Vec::new(); + entry_mutex!(self.players_banned, |guard| { + for account in guard.keys().into_iter() { + vec.push(account.clone()); + } + }); + vec + } + + /// Check if account is banned + pub fn is_account_banned(&self, account: &Account) -> bool { + entry_mutex!(self.players_banned, |guard| { + if guard.contains_key(account) { + true; + } + }); + false + } + + /// Get service type of account + pub fn get_service_type(&self, account: &Account) -> Option { + let mut result = None; + entry_mutex!(self.account_service_type, |guard| { + result = guard.get(account).cloned(); + }); + result + } +} + +impl GameControlRuntime { + + /// Process a control message + fn process_control_message(&mut self, who: &Account, msg: ControlMessage) -> Result<(), ControlMessage> { + match msg { + Msg(_) => { + self.send_event(who, msg); + Ok(()) + } + + Pressed(button_key) => { + let key_valid = self.check_key(&self.keys.button_keys, &button_key); + if key_valid { + Self::change_value(&mut self.button, button_key, who, true); + self.send_event(who, msg); + trace!("[Control Runtime] Player \"{}\" pressed btn_{}", &who.id, button_key); + } else { + warn!("[Control Runtime] Key btn_{} not registered!", button_key); + } + Ok(()) + } + + Released(button_key) => { + if self.check_key(&self.keys.button_keys, &button_key) { + Self::change_value(&mut self.button, button_key, who, false); + self.send_event(who, msg); + trace!("[Control Runtime] Player \"{}\" released btn_{}", &who.id, button_key); + } else { + warn!("[Control Runtime] Key btn_{} not registered!", button_key); + } + Ok(()) + } + + Axis(axis_key, axis) => { + if self.check_key(&self.keys.button_keys, &axis_key) { + Self::change_value(&mut self.axes, axis_key, who, axis); + trace!("[Control Runtime] Player \"{}\" changed ax_{} to ({})", &who.id, axis_key, axis); + } else { + warn!("[Control Runtime] Key ax_{} not registered!", axis_key); + } + Ok(()) + } + + Dir(dir_key, dir) => { + if self.check_key(&self.keys.button_keys, &dir_key) { + Self::change_value(&mut self.directions, dir_key, who, dir); + trace!("[Control Runtime] Player \"{}\" changed dir_{} to ({}, {})", &who.id, dir_key, dir.0, dir.1); + } else { + warn!("[Control Runtime] Key dir_{} not registered!", dir_key); + } + Ok(()) + } + + _ => { + Err(msg) + } + } + } + + /// Get specified player's direction value + pub fn get_direction(&self, who: &Account, key: &u8) -> Option<(f64, f64)> { + Self::get(&self.directions, who, key) + } + + /// Get specified player's axis value + pub fn get_axis(&self, who: &Account, key: &u8) -> Option { + Self::get(&self.axes, who, key) + } + + /// Get specified player's button status + pub fn get_button_status(&self, who: &Account, key: &u8) -> Option { + Self::get(&self.button, who, key) + } + + fn check_key(&self, map: &HashMap, key: &u8) -> bool { + map.contains_key(key) + } + + fn get(map: &HashMap>, who: &Account, key: &u8) -> Option { + let key = map.get(key); + if key.is_some() { + let value = key.unwrap().get(who); + if value.is_some() { + let result = value.unwrap(); + Some(result.clone()) + } else { None } + } else { None } + } + + fn change_value(map: &mut HashMap>, key: u8, who: &Account, msg: T) { + map.entry(key) + .or_insert_with(HashMap::new) + .insert(who.clone(), msg); + } + + fn send_event(&mut self, who: &Account, msg: ControlMessage) { + self.events.push_back((who.clone(), msg)); + } +} \ No newline at end of file diff --git a/core/src/data/game/implements.rs b/core/src/data/game/implements.rs deleted file mode 100644 index ad86572..0000000 --- a/core/src/data/game/implements.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::sync::{Arc, Mutex}; -use nogamepads::entry_mutex; -use crate::data::game::runtime::structs::{GameControlRuntime, GameRuntime, GameRuntimeData}; -use crate::data::game::structs::{GameControlData, GameData, GameRuntimeDataArchive}; -use crate::data::game::types::{GameInfo, Players}; -use crate::data::player::structs::Player; - -impl Default for GameData { - fn default() -> Self { - GameData::new() - } -} - -impl GameData { - - /// Create new game data - pub fn new() -> GameData { - let mut game = GameData { - info: GameInfo::default(), - control: GameControlData::default(), - archive: GameRuntimeDataArchive::default(), - }; - - game.name("Mini Hero".to_string()); - game.version(env!("PROJECT_VERSION").to_string()); - game - } - - /// Add or modify game name information - pub fn name(&mut self, name: String) -> &mut GameData { - self.info("Game_Name".to_string(), name); - self - } - - /// Add or modify game version information - pub fn version(&mut self, version: String) -> &mut GameData { - self.info("Version".to_string(), version); - self - } - - /// Add or modify information for a specific entry - pub fn info(&mut self, name: String, value: String) -> &mut GameData { - self.info.insert(name, value); - self - } - - /// Read game runtime archive data - pub fn load_data(&mut self, storage: GameRuntimeDataArchive) -> &mut GameData { - self.archive = storage; - self - } - - /// Build the game-side runtime using game data - pub fn runtime(self) -> Arc> { - let runtime = GameRuntime { - info: self.info, - data: self.archive.into(), - control: GameControlRuntime { - keys: self.control, - ..Default::default() - }, - - writer_count: 0, - reader_count: 0, - }; - Arc::new(Mutex::new(runtime)) - } -} - -impl From for GameRuntimeData { - fn from(archive: GameRuntimeDataArchive) -> Self { - let banned_mutex = Players::default(); - entry_mutex!(banned_mutex, |guard| { - for account in archive.banned { - let player_info = Player::from(account.clone()); - guard.entry(account).or_insert_with(|| player_info); - } - }); - GameRuntimeData { - players_banned : banned_mutex, - ..Self::default() - } - } -} - -impl From for GameRuntimeDataArchive { - fn from(data: GameRuntimeData) -> Self { - let mut banned = Vec::new(); - entry_mutex!(data.players_online, |guard| { - for account in guard.keys().into_iter() { - banned.push(account.to_owned()); - } - }); - - GameRuntimeDataArchive { - banned - } - } -} \ No newline at end of file diff --git a/core/src/data/game/mod.rs b/core/src/data/game/mod.rs index 4a0a3d4..1a31cf1 100644 --- a/core/src/data/game/mod.rs +++ b/core/src/data/game/mod.rs @@ -1,6 +1,5 @@ -pub mod cli; -pub mod runtime; +pub mod game_cli; -pub mod implements; -pub mod structs; +pub mod game_data; +pub mod game_runtime; pub mod types; \ No newline at end of file diff --git a/core/src/data/game/runtime/implements.rs b/core/src/data/game/runtime/implements.rs deleted file mode 100644 index 22b6a5d..0000000 --- a/core/src/data/game/runtime/implements.rs +++ /dev/null @@ -1,376 +0,0 @@ -use std::collections::{HashMap, VecDeque}; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering::SeqCst; -use log::{info, trace, warn}; -use nogamepads::entry_mutex; -use crate::data::game::runtime::structs::{GameControlRuntime, GameRuntime, GameRuntimeData}; -use crate::data::game::types::Players; -use crate::data::message::enums::{JoinFailedMessage, ControlMessage, ExitReason, GameMessage}; -use crate::data::message::enums::JoinFailedMessage::{ContainIdenticalPlayer, GameLocked, PlayerBanned}; -use crate::data::message::enums::ControlMessage::{Axis, Dir, Msg, Pressed, Released}; -use crate::data::message::enums::ExitReason::{YouAreBanned, YouAreKicked}; -use crate::data::message::enums::GameMessage::{EventTrigger, LetExit}; -use crate::data::message::traits::MessageManager; -use crate::data::player::structs::{Account, Player}; -use crate::service::service_types::ServiceType; -use crate::service::service_types::ServiceType::TCPConnection; - -impl GameRuntime { - - /// Attempt to have the specified player join the game - pub fn try_join_player(&mut self, player: Player) -> Result<(), JoinFailedMessage> { - let join = self.can_join_game(&player.account); - match join { - Ok(_) => { - self.data.sign_player_online_status(&player, TCPConnection, true); - trace!("[Game Runtime] Player \"{}\" joined", player.account); - Ok(()) - } - Err(why) => { - warn!("[Game Runtime] Player \"{}\" join failed: {:?}", player.account, why); - Err(why) - } - } - } - - fn can_join_game(&self, account: &Account) -> Result { - - if self.is_game_locked() { - Err(GameLocked) - } else if self.data.is_account_banned(account) { - Err(PlayerBanned) - } else if self.data.is_account_online(account) { - Err(ContainIdenticalPlayer) - } else { - Ok(true) - } - } - - /// Request an account to exit - pub fn let_account_exit(&mut self, account: &Account, reason: ExitReason, service_type: ServiceType) { - // Send a leave message to the pad_client and wait for it to actively disconnect - if self.data.is_account_online(account) { - self.send((account.clone(), LetExit(reason)), account.clone(), service_type); - } - } - - pub fn kick_player(&mut self, player: &Player, service_type: ServiceType) { - // Send a leave message to the pad_client and wait for it to actively disconnect - if self.data.is_account_online(&player.account) { - self.send((player.account.clone(), LetExit(YouAreKicked)), player.account.clone(), service_type); - } - } - - pub fn ban_player(&mut self, player: &Player, service_type: ServiceType) { - if self.data.is_account_online(&player.account) { - self.send((player.account.clone(), LetExit(YouAreBanned)), player.account.clone(), service_type); - entry_mutex!(self.data.players_banned, |guard| { - guard.insert(player.account.clone(), player.clone()); - }); - } - } - - pub fn pardon_player(&mut self, player: &Player) { - entry_mutex!(self.data.players_banned, |guard| { - guard.remove(&player.account); - }); - } - - /// Check if the game is locked - pub fn is_game_locked(&self) -> bool { - self.data.locked.load(SeqCst) - } - - /// Lock the game - pub fn lock_game(&self) { - if !self.data.locked.load(SeqCst) { - self.data.locked.store(true, SeqCst); - info!("[Game Runtime] Game locked!"); - } - } - - /// Unlock the game - pub fn unlock_game(&self) { - if self.data.locked.load(SeqCst) { - self.data.locked.store(false, SeqCst); - info!("[Game Runtime] Game unlocked!"); - } - } - - /// Close the Game - pub fn close_game(&self) { - if !self.data.close.load(SeqCst) { - self.data.close.store(true, SeqCst); - info!("[Game Runtime] Game closed!"); - } - } - - /// Send a GameMessage to account - pub fn send_game_message(&mut self, account: &Account, message: GameMessage, service_type: ServiceType) { - self.send((account.clone(), message), account.clone(), service_type); - } - - pub fn send_event(&mut self, account: &Account, event_trigger: u8, service_type: ServiceType) { - self.send_game_message(account, EventTrigger(event_trigger), service_type); - } - - pub fn send_message(&mut self, account: &Account, message: String, service_type: ServiceType) { - self.send_game_message(account, GameMessage::Msg(message), service_type); - } - - /// Pop an event message - pub fn pop_event(&mut self) -> Option<(Account, ControlMessage)> { - let pop = self.control.events.pop_front(); - if pop.is_some() { - let (account, msg) = pop.unwrap(); - if self.data.is_account_online(&account) { - trace!("[Control Runtime] Message: {:?} from \"{}\" ", &msg, account); - Some((account, msg)) - } else { - warn!("[Control Runtime] Invalid message: Player \"{}\" is not online!", account); - None - } - } else { - None - } - } -} - -/// Message manager for game pad_client runtime -/// After the service starts, it can be accessed or relevant messages can be stored. -impl MessageManager<(Account, ControlMessage), (Account, GameMessage), Account> for GameRuntime { - fn borrow_received_list_mut(&mut self) -> &mut HashMap<(ServiceType, Account), VecDeque<(Account, ControlMessage)>> { - &mut self.data.received - } - - fn borrow_send_list_mut(&mut self) -> &mut HashMap<(ServiceType, Account), VecDeque<(Account, GameMessage)>> { - &mut self.data.send - } - - fn pop_from_send_list(&mut self, key: Account, service: ServiceType) -> Option<(Account, GameMessage)> { - let key = (service, key); - self.borrow_send_list_mut() - .entry(key) - .or_insert_with(VecDeque::new) - .pop_front() - } - - fn put_into_receive_list(&mut self, message: (Account, ControlMessage), _key: Account, _service: ServiceType) { - let result = self.control.process_control_message(&message.0, message.1); - if result.is_err() { - let result = result.unwrap_err(); - warn!("[Game Runtime] Can't process message: {:?}", result); - drop(result); - } - } -} - -impl Default for GameRuntimeData { - fn default() -> Self { - Self { - received: Default::default(), - send: Default::default(), - players_online: Players::default(), - players_banned: Players::default(), - account_service_type: Default::default(), - - locked: AtomicBool::new(false), - close: AtomicBool::new(false) - } - } -} - -impl GameRuntimeData { - - /// Mark a player as online - pub fn sign_player_online_status(&mut self, player: &Player, service_type: ServiceType, value: bool) { - let online = self.is_account_online(&player.account); - if online && !value { - - // Remove player - entry_mutex!(self.players_online, |guard| { - guard.remove_entry(&player.account); - }); - - info!("[Game Runtime] Signed player \"{}\" is [OFFLINE]!", player.account); - - // Reset runtime - let key = (service_type, player.account.clone()); - let get_received = self.received.get_mut(&key); - let get_send = self.send.get_mut(&key); - if let Some(list) = get_received { - list.clear(); - } - if let Some(list) = get_send { - list.clear(); - } - - } else if !online & value { - - // Insert player - entry_mutex!(self.players_online, |guard| { - guard.entry(player.account.clone()) - .or_insert_with(|| player.clone()); - }); - - info!("[Game Runtime] Signed player \"{}\" is [ONLINE]!", player.account); - - // Record service type - entry_mutex!(self.account_service_type, |guard| { - guard.entry(player.account.clone()) - .or_insert_with(|| TCPConnection); - }) - } - } - - /// Returns all online accounts - pub fn online_accounts(&self) -> Vec { - let mut vec = Vec::new(); - entry_mutex!(self.players_online, |guard| { - for account in guard.keys().into_iter() { - vec.push(account.clone()); - } - }); - vec - } - - /// Check if specified account is online - pub fn is_account_online(&self, account: &Account) -> bool { - entry_mutex!(self.players_online, |guard| { - if guard.contains_key(account) { - return true; - } - }); - false - } - - /// Returns all banned accounts - pub fn banned_accounts(&self) -> Vec { - let mut vec = Vec::new(); - entry_mutex!(self.players_banned, |guard| { - for account in guard.keys().into_iter() { - vec.push(account.clone()); - } - }); - vec - } - - /// Check if account is banned - pub fn is_account_banned(&self, account: &Account) -> bool { - entry_mutex!(self.players_banned, |guard| { - if guard.contains_key(account) { - true; - } - }); - false - } - - /// Get service type of account - pub fn get_service_type(&self, account: &Account) -> Option { - let mut result = None; - entry_mutex!(self.account_service_type, |guard| { - result = guard.get(account).cloned(); - }); - result - } -} - -impl GameControlRuntime { - - /// Process a control message - fn process_control_message(&mut self, who: &Account, msg: ControlMessage) -> Result<(), ControlMessage> { - match msg { - Msg(_) => { - self.send_event(who, msg); - Ok(()) - } - - Pressed(button_key) => { - let key_valid = self.check_key(&self.keys.button_keys, &button_key); - if key_valid { - Self::change_value(&mut self.button, button_key, who, true); - self.send_event(who, msg); - trace!("[Control Runtime] Player \"{}\" pressed btn_{}", &who.id, button_key); - } else { - warn!("[Control Runtime] Key btn_{} not registered!", button_key); - } - Ok(()) - } - - Released(button_key) => { - if self.check_key(&self.keys.button_keys, &button_key) { - Self::change_value(&mut self.button, button_key, who, false); - self.send_event(who, msg); - trace!("[Control Runtime] Player \"{}\" released btn_{}", &who.id, button_key); - } else { - warn!("[Control Runtime] Key btn_{} not registered!", button_key); - } - Ok(()) - } - - Axis(axis_key, axis) => { - if self.check_key(&self.keys.button_keys, &axis_key) { - Self::change_value(&mut self.axes, axis_key, who, axis); - trace!("[Control Runtime] Player \"{}\" changed ax_{} to ({})", &who.id, axis_key, axis); - } else { - warn!("[Control Runtime] Key ax_{} not registered!", axis_key); - } - Ok(()) - } - - Dir(dir_key, dir) => { - if self.check_key(&self.keys.button_keys, &dir_key) { - Self::change_value(&mut self.directions, dir_key, who, dir); - trace!("[Control Runtime] Player \"{}\" changed dir_{} to ({}, {})", &who.id, dir_key, dir.0, dir.1); - } else { - warn!("[Control Runtime] Key dir_{} not registered!", dir_key); - } - Ok(()) - } - - _ => { - Err(msg) - } - } - } - - /// Get specified player's direction value - pub fn get_direction(&self, who: &Account, key: &u8) -> Option<(f64, f64)> { - Self::get(&self.directions, who, key) - } - - /// Get specified player's axis value - pub fn get_axis(&self, who: &Account, key: &u8) -> Option { - Self::get(&self.axes, who, key) - } - - /// Get specified player's button status - pub fn get_button_status(&self, who: &Account, key: &u8) -> Option { - Self::get(&self.button, who, key) - } - - fn check_key(&self, map: &HashMap, key: &u8) -> bool { - map.contains_key(key) - } - - fn get(map: &HashMap>, who: &Account, key: &u8) -> Option { - let key = map.get(key); - if key.is_some() { - let value = key.unwrap().get(who); - if value.is_some() { - let result = value.unwrap(); - Some(result.clone()) - } else { None } - } else { None } - } - - fn change_value(map: &mut HashMap>, key: u8, who: &Account, msg: T) { - map.entry(key) - .or_insert_with(HashMap::new) - .insert(who.clone(), msg); - } - - fn send_event(&mut self, who: &Account, msg: ControlMessage) { - self.events.push_back((who.clone(), msg)); - } -} \ No newline at end of file diff --git a/core/src/data/game/runtime/mod.rs b/core/src/data/game/runtime/mod.rs deleted file mode 100644 index 0ff870f..0000000 --- a/core/src/data/game/runtime/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod implements; -pub mod structs; \ No newline at end of file diff --git a/core/src/data/game/runtime/structs.rs b/core/src/data/game/runtime/structs.rs deleted file mode 100644 index c6e005f..0000000 --- a/core/src/data/game/runtime/structs.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::collections::{HashMap, VecDeque}; -use std::sync::atomic::AtomicBool; -use std::sync::Mutex; -use crate::data::game::structs::GameControlData; -use crate::data::game::types::{GameInfo, Players}; -use crate::data::message::enums::{ControlMessage, GameMessage}; -use crate::data::player::structs::Account; -use crate::service::service_types::ServiceType; - -/// Game pad_client runtime -/// Stores the game state, player information, and all data involved in controller-side interactions during runtime -pub struct GameRuntime { - - pub info: GameInfo, - pub data: GameRuntimeData, - pub control: GameControlRuntime, - - pub writer_count: i32, - pub reader_count: i32, -} - -pub struct GameRuntimeData { - - pub(crate) received: HashMap<(ServiceType, Account), VecDeque<(Account, ControlMessage)>>, - pub(crate) send: HashMap<(ServiceType, Account), VecDeque<(Account, GameMessage)>>, - - pub(crate) players_online: Players, - pub(crate) players_banned: Players, - pub(crate) account_service_type: Mutex>, - - pub locked: AtomicBool, - pub close: AtomicBool, -} - -#[derive(Default)] -pub struct GameControlRuntime { - pub(crate) keys: GameControlData, - pub(crate) directions : HashMap>, - pub(crate) axes : HashMap>, - pub(crate) button : HashMap>, - pub(crate) events : VecDeque<(Account, ControlMessage)> -} \ No newline at end of file diff --git a/core/src/data/game/structs.rs b/core/src/data/game/structs.rs deleted file mode 100644 index 52fc0f1..0000000 --- a/core/src/data/game/structs.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::collections::HashMap; -use serde::{Deserialize, Serialize}; -use crate::data::game::types::GameInfo; -use crate::data::player::structs::Account; - -/// Game pad_client data -/// Describes the basic information of the game pad_client -#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] -pub struct GameData { - pub info: GameInfo, - pub control: GameControlData, - pub archive: GameRuntimeDataArchive -} - -/// Game control information -/// Describes the buttons, axes, and directions that can be controlled. -#[derive(Default, Clone, Serialize, Deserialize, PartialEq, Debug)] -pub struct GameControlData { - pub direction_keys : HashMap, - pub axis_keys : HashMap, - pub button_keys : HashMap, -} - -/// Archive of game runtime data -/// The game pad_client can convert data into this structure for persistence. -#[derive(Default, Clone, Serialize, Deserialize, PartialEq, Debug)] -pub struct GameRuntimeDataArchive { - pub banned: Vec -} \ No newline at end of file diff --git a/core/src/data/game/types.rs b/core/src/data/game/types.rs index 8af17d3..778201f 100644 --- a/core/src/data/game/types.rs +++ b/core/src/data/game/types.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::sync::Mutex; -use crate::data::player::structs::{Account, Player}; +use crate::data::player::player_data::{Account, Player}; pub(crate) type GameInfo = HashMap; diff --git a/core/src/data/message/enums.rs b/core/src/data/message/enums.rs deleted file mode 100644 index 4150b63..0000000 --- a/core/src/data/message/enums.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::data::player::structs::Player; -use bincode::{Decode, Encode}; -use crate::data::game::types::GameInfo; - -/// Control messages. -/// Messages sent from controller to game pad_client after establishing persistent connection -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum ControlMessage { - /// Plain message containing a string - /// The message will be handed over to the game for its own processing - Msg(String), - - /// Press event - /// Indicates that a button has been pressed - Pressed(u8), - - /// Release event - /// Indicates that a button has been released - Released(u8), - - /// Axis input - /// Indicates that the value of an axis has been changed - Axis(u8, f64), - - /// Directional input - /// Indicates that the value of a direction has been changed - Dir(u8, (f64, f64)), - - /// Exit command - /// Sends a disconnect request to the pad_server - Exit, - - #[default] - /// Error state - Err, - - /// Indicates the termination message, which is the final message in a long-lived connection. - End -} - -/// Game messages. -/// Messages sent from game pad_client to controller after establishing persistent connection -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum GameMessage { - /// Event trigger - /// Sends an event to the controller; if skins are enabled, this will trigger corresponding animations, sounds, vibrations, etc. - EventTrigger(u8), - - /// Plain message containing a string - /// The message will be handed over to the controller for its own processing - Msg(String), - - /// Disconnect request - /// Notifies the pad_client that the connection will be terminated - LetExit(ExitReason), - - /// Error state - #[default] - Err, - - /// Indicates the termination message, which is the final message in a long-lived connection. - End -} - -/// Exit reasons. -/// Reason provided when requesting disconnection -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum ExitReason { - /// Normal exit - /// No specific reason, simply requesting to disconnect - Exit, - - /// Game has ended - GameOver, - - /// Server shutdown (normal) - ServerClosed, - - /// Kicked by pad_server - YouAreKicked, - - /// Account banned - YouAreBanned, - - /// Error state - #[default] - Err -} - -/// Connection messages. -/// Messages sent by pad_client when requesting pad_server connection -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum ConnectionMessage { - /// Requests to join the game - Join(Player), - - /// Request for game information - RequestGameInfos, - - /// Request for game layout configuration file - RequestLayoutConfigure, - - /// Request to download game skin assets - RequestSkinPackage, - - /// Ready state to establish persistent connection - Ready, - - /// Error state - #[default] - Err -} - -/// Connection Response. -/// Messages from pad_server responding to pad_client connection requests -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum ConnectionResponseMessage { - /// Game information data - GameInfos(GameInfo), - - /// Rejection with reason - Deny(JoinFailedMessage), - - /// Failure with reason - Fail(JoinFailedMessage), - - /// Approval confirmation - Ok, - - /// Welcome acknowledgment - Welcome, - - /// Error state - #[default] - Err -} - -/// Game Join Failure Information. -/// Reason provided when pad_client fails to join -#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] -pub enum JoinFailedMessage { - /// Game already contains identical player - ContainIdenticalPlayer, - - /// Player is banned - PlayerBanned, - - /// Game is locked, no further joins allowed - GameLocked, - - /// Unknown error - #[default] - UnknownError -} \ No newline at end of file diff --git a/core/src/data/message/implements.rs b/core/src/data/message/implements.rs deleted file mode 100644 index 2d5bae8..0000000 --- a/core/src/data/message/implements.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::data::message::enums::{ConnectionResponseMessage, JoinFailedMessage, ConnectionMessage, ControlMessage, ExitReason, GameMessage}; -use crate::data::message::traits::MessageEncoder; - -#[macro_export] -macro_rules! encoder { - ($($msg:ident),+) => { - $( - impl MessageEncoder<$msg> for $msg {} - )+ - }; -} - -encoder!( - ControlMessage, GameMessage, ExitReason, ConnectionMessage, ConnectionResponseMessage, JoinFailedMessage -); \ No newline at end of file diff --git a/core/src/data/message/message_encoders.rs b/core/src/data/message/message_encoders.rs new file mode 100644 index 0000000..d20e74a --- /dev/null +++ b/core/src/data/message/message_encoders.rs @@ -0,0 +1,15 @@ +use crate::data::message::message_enums::{ConnectionResponseMessage, JoinFailedMessage, ConnectionMessage, ControlMessage, ExitReason, GameMessage}; +use crate::data::message::traits::MessageEncoder; + +#[macro_export] +macro_rules! encoder { + ($($msg:ident),+) => { + $( + impl MessageEncoder<$msg> for $msg {} + )+ + }; +} + +encoder!( + ControlMessage, GameMessage, ExitReason, ConnectionMessage, ConnectionResponseMessage, JoinFailedMessage +); \ No newline at end of file diff --git a/core/src/data/message/message_enums.rs b/core/src/data/message/message_enums.rs new file mode 100644 index 0000000..fd369a9 --- /dev/null +++ b/core/src/data/message/message_enums.rs @@ -0,0 +1,154 @@ +use bincode::{Decode, Encode}; +use crate::data::game::types::GameInfo; +use crate::data::player::player_data::Player; + +/// Control messages. +/// Messages sent from controller to game pad_client after establishing persistent connection +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum ControlMessage { + /// Plain message containing a string + /// The message will be handed over to the game for its own processing + Msg(String), + + /// Press event + /// Indicates that a button has been pressed + Pressed(u8), + + /// Release event + /// Indicates that a button has been released + Released(u8), + + /// Axis input + /// Indicates that the value of an axis has been changed + Axis(u8, f64), + + /// Directional input + /// Indicates that the value of a direction has been changed + Dir(u8, (f64, f64)), + + /// Exit command + /// Sends a disconnect request to the pad_server + Exit, + + #[default] + /// Error state + Err, + + /// Indicates the termination message, which is the final message in a long-lived connection. + End +} + +/// Game messages. +/// Messages sent from game pad_client to controller after establishing persistent connection +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum GameMessage { + /// Event trigger + /// Sends an event to the controller; if skins are enabled, this will trigger corresponding animations, sounds, vibrations, etc. + EventTrigger(u8), + + /// Plain message containing a string + /// The message will be handed over to the controller for its own processing + Msg(String), + + /// Disconnect request + /// Notifies the pad_client that the connection will be terminated + LetExit(ExitReason), + + /// Error state + #[default] + Err, + + /// Indicates the termination message, which is the final message in a long-lived connection. + End +} + +/// Exit reasons. +/// Reason provided when requesting disconnection +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum ExitReason { + /// Normal exit + /// No specific reason, simply requesting to disconnect + Exit, + + /// Game has ended + GameOver, + + /// Server shutdown (normal) + ServerClosed, + + /// Kicked by pad_server + YouAreKicked, + + /// Account banned + YouAreBanned, + + /// Error state + #[default] + Err +} + +/// Connection messages. +/// Messages sent by pad_client when requesting pad_server connection +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum ConnectionMessage { + /// Requests to join the game + Join(Player), + + /// Request for game information + RequestGameInfos, + + /// Request for game layout configuration file + RequestLayoutConfigure, + + /// Request to download game skin assets + RequestSkinPackage, + + /// Ready state to establish persistent connection + Ready, + + /// Error state + #[default] + Err +} + +/// Connection Response. +/// Messages from pad_server responding to pad_client connection requests +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum ConnectionResponseMessage { + /// Game information data + GameInfos(GameInfo), + + /// Rejection with reason + Deny(JoinFailedMessage), + + /// Failure with reason + Fail(JoinFailedMessage), + + /// Approval confirmation + Ok, + + /// Welcome acknowledgment + Welcome, + + /// Error state + #[default] + Err +} + +/// Game Join Failure Information. +/// Reason provided when pad_client fails to join +#[derive(Default, Encode, Decode, PartialEq, Debug, Clone)] +pub enum JoinFailedMessage { + /// Game already contains identical player + ContainIdenticalPlayer, + + /// Player is banned + PlayerBanned, + + /// Game is locked, no further joins allowed + GameLocked, + + /// Unknown error + #[default] + UnknownError +} \ No newline at end of file diff --git a/core/src/data/message/mod.rs b/core/src/data/message/mod.rs index 96fdd73..1b9d2fd 100644 --- a/core/src/data/message/mod.rs +++ b/core/src/data/message/mod.rs @@ -1,3 +1,3 @@ -pub mod enums; -pub mod implements; +pub mod message_encoders; +pub mod message_enums; pub mod traits; \ No newline at end of file diff --git a/core/src/data/player/implements.rs b/core/src/data/player/implements.rs deleted file mode 100644 index bbaefc4..0000000 --- a/core/src/data/player/implements.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::data::player::structs::{Account, Customize, Player}; -use crate::data::player::ACCOUNT_HASH_SALT; -use hex::encode; -use sha1::{Digest, Sha1}; -use std::fmt::{Display, Formatter}; -use std::hash::{Hash, Hasher}; -use nogamepads::string_utils::process_id_text; - -impl Player { - - /// Create new player information using a username and password - pub fn register(id: String, password: String) -> Player { - let mut player = Player { - customize: None, - account: Account::default() - }; - - let processed_id = process_id_text(id); - - player.account.id = processed_id.clone(); - player.account.player_hash = Self::gen_hash(processed_id, password); - player - } - - pub fn check(&self, password: String) -> bool { - let hash = Self::gen_hash(self.account.id.clone(), password); - hash == self.account.player_hash - } - - fn gen_hash(processed_id: String, password: String) -> String { - let combined = format!("{}{}{}", processed_id, password, ACCOUNT_HASH_SALT); - let mut hasher = Sha1::new(); - hasher.update(combined); - let result = hasher.finalize(); - encode(&result[..]) - } -} - -// Customize implements -impl Player { - - /// Set player nickname - pub fn nickname(&mut self, name: &String) -> &mut Player { - self.change(|custom| { - custom.nickname = name.clone(); - custom - }) - } - - /// Set the hue of the player's color - pub fn hue(&mut self, mut hue: i32) -> &mut Player { - hue = hue.clamp(0, 360); - self.change(|custom| { - custom.color_hue = hue.clone(); - custom - }) - } - - /// Set the player's HSV values - pub fn hsv(&mut self, mut hue: i32, mut saturation: f64, mut value: f64) -> &mut Player { - hue = hue.clamp(0, 360); - saturation = saturation.clamp(0.0, 1.0); - value = value.clamp(0.0, 1.0); - self.change(|custom| { - custom.color_hue = hue.clone(); - custom.color_saturation = saturation.clone(); - custom.color_value = value.clone(); - custom - }) - } - - fn init(&mut self) { - if self.customize.is_none() { - self.customize = Some(Customize::default()); - } - } - - fn change(&mut self, f: F) -> &mut Player - where F: FnOnce(&mut Customize) -> &mut Customize { - self.init(); - let mut customize = self.customize.clone().unwrap(); - f(&mut customize); - self.customize = Some(customize); - self - } -} - -impl PartialEq for Player { - fn eq(&self, other: &Self) -> bool { - self.account == other.account - } -} - -impl Hash for Player { - fn hash(&self, state: &mut H) { - self.account.hash(state); - } -} - -impl Display for Player { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.account.id.as_str()) - } -} - -impl From for Player { - fn from(account: Account) -> Self { - Player { - account, - customize: None - } - } -} - -impl Display for Account { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.id.as_str()) - } -} \ No newline at end of file diff --git a/core/src/data/player/mod.rs b/core/src/data/player/mod.rs index 7e8fe28..ff6ae85 100644 --- a/core/src/data/player/mod.rs +++ b/core/src/data/player/mod.rs @@ -1,4 +1,3 @@ -pub mod implements; -pub mod structs; +pub mod player_data; pub const ACCOUNT_HASH_SALT : &str = env!("TEST_PLAYER_ACCOUNT"); \ No newline at end of file diff --git a/core/src/data/player/player_data.rs b/core/src/data/player/player_data.rs new file mode 100644 index 0000000..1fe5ff0 --- /dev/null +++ b/core/src/data/player/player_data.rs @@ -0,0 +1,162 @@ +use crate::data::player::ACCOUNT_HASH_SALT; +use hex::encode; +use sha1::{Digest, Sha1}; +use std::fmt::{Display, Formatter}; +use std::hash::{Hash, Hasher}; +use bincode::{Decode, Encode}; +use serde::{Deserialize, Serialize}; +use nogamepads::string_utils::process_id_text; + +/// Player information +/// Describes a player's specific details, which are frequently exchanged between the controller and game pad_client. +#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, Debug)] +pub struct Player { + + /// Account information + pub account: Account, + + /// Custom information (Optional) + pub customize: Option +} + +/// Account information +/// Essential data for verifying player uniqueness, including the player's hash value and account ID. +#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, Eq, Hash, PartialEq, Debug)] +pub struct Account { + + /// Player name stored in data, allowing only lowercase letters and underscores + pub id: String, + + /// Player hash value proving player uniqueness + pub player_hash: String +} + +/// Custom information +/// Describes personalized player details displayed in-game, such as name, color, or other customizations. +#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, PartialEq, Debug)] +pub struct Customize { + + /// Player name displayed in the game + pub nickname: String, + + /// HSV Color - Hue (Range: 0 - 360) + pub color_hue: i32, + + /// HSV Color - Saturation (Range: 0 - 1) + pub color_saturation: f64, + + /// HSV Color - Value (Range: 0 - 1) + pub color_value: f64 +} + +impl Player { + + /// Create new player information using a username and password + pub fn register(id: String, password: String) -> Player { + let mut player = Player { + customize: None, + account: Account::default() + }; + + let processed_id = process_id_text(id); + + player.account.id = processed_id.clone(); + player.account.player_hash = Self::gen_hash(processed_id, password); + player + } + + pub fn check(&self, password: String) -> bool { + let hash = Self::gen_hash(self.account.id.clone(), password); + hash == self.account.player_hash + } + + fn gen_hash(processed_id: String, password: String) -> String { + let combined = format!("{}{}{}", processed_id, password, ACCOUNT_HASH_SALT); + let mut hasher = Sha1::new(); + hasher.update(combined); + let result = hasher.finalize(); + encode(&result[..]) + } +} + +// Customize implements +impl Player { + + /// Set player nickname + pub fn nickname(&mut self, name: &String) -> &mut Player { + self.change(|custom| { + custom.nickname = name.clone(); + custom + }) + } + + /// Set the hue of the player's color + pub fn hue(&mut self, mut hue: i32) -> &mut Player { + hue = hue.clamp(0, 360); + self.change(|custom| { + custom.color_hue = hue.clone(); + custom + }) + } + + /// Set the player's HSV values + pub fn hsv(&mut self, mut hue: i32, mut saturation: f64, mut value: f64) -> &mut Player { + hue = hue.clamp(0, 360); + saturation = saturation.clamp(0.0, 1.0); + value = value.clamp(0.0, 1.0); + self.change(|custom| { + custom.color_hue = hue.clone(); + custom.color_saturation = saturation.clone(); + custom.color_value = value.clone(); + custom + }) + } + + fn init(&mut self) { + if self.customize.is_none() { + self.customize = Some(Customize::default()); + } + } + + fn change(&mut self, f: F) -> &mut Player + where F: FnOnce(&mut Customize) -> &mut Customize { + self.init(); + let mut customize = self.customize.clone().unwrap(); + f(&mut customize); + self.customize = Some(customize); + self + } +} + +impl PartialEq for Player { + fn eq(&self, other: &Self) -> bool { + self.account == other.account + } +} + +impl Hash for Player { + fn hash(&self, state: &mut H) { + self.account.hash(state); + } +} + +impl Display for Player { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.account.id.as_str()) + } +} + +impl From for Player { + fn from(account: Account) -> Self { + Player { + account, + customize: None + } + } +} + +impl Display for Account { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.id.as_str()) + } +} \ No newline at end of file diff --git a/core/src/data/player/structs.rs b/core/src/data/player/structs.rs deleted file mode 100644 index cc3d7c9..0000000 --- a/core/src/data/player/structs.rs +++ /dev/null @@ -1,45 +0,0 @@ -use bincode::{Decode, Encode}; -use serde::{Deserialize, Serialize}; -use std::hash::{Hash}; - -/// Player information -/// Describes a player's specific details, which are frequently exchanged between the controller and game pad_client. -#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, Debug)] -pub struct Player { - - /// Account information - pub account: Account, - - /// Custom information (Optional) - pub customize: Option -} - -/// Account information -/// Essential data for verifying player uniqueness, including the player's hash value and account ID. -#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, Eq, Hash, PartialEq, Debug)] -pub struct Account { - - /// Player name stored in data, allowing only lowercase letters and underscores - pub id: String, - - /// Player hash value proving player uniqueness - pub player_hash: String -} - -/// Custom information -/// Describes personalized player details displayed in-game, such as name, color, or other customizations. -#[derive(Default, Clone, Encode, Decode, Serialize, Deserialize, PartialEq, Debug)] -pub struct Customize { - - /// Player name displayed in the game - pub nickname: String, - - /// HSV Color - Hue (Range: 0 - 360) - pub color_hue: i32, - - /// HSV Color - Saturation (Range: 0 - 1) - pub color_saturation: f64, - - /// HSV Color - Value (Range: 0 - 1) - pub color_value: f64 -} \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index 49db429..81d1155 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -5,4 +5,4 @@ pub mod data; pub mod service; pub const BINCODE_CONVERT_FAILED : Vec = Vec::new(); -pub const BINCODE_CONFIG : Configuration = config::standard(); +pub const BINCODE_CONFIG : Configuration = config::standard(); \ No newline at end of file diff --git a/core/src/message_encoders.rs b/core/src/message_encoders.rs new file mode 100644 index 0000000..49db429 --- /dev/null +++ b/core/src/message_encoders.rs @@ -0,0 +1,8 @@ +use bincode::config; +use bincode::config::Configuration; + +pub mod data; +pub mod service; + +pub const BINCODE_CONVERT_FAILED : Vec = Vec::new(); +pub const BINCODE_CONFIG : Configuration = config::standard(); diff --git a/core/src/service/tcp_network/long_connection.rs b/core/src/service/tcp_network/long_connection.rs index 200cf61..c8916fa 100644 --- a/core/src/service/tcp_network/long_connection.rs +++ b/core/src/service/tcp_network/long_connection.rs @@ -1,6 +1,3 @@ -use crate::data::player::structs::Player; -use crate::service::tcp_network::pad_client::structs::PadClientNetwork; -use crate::service::tcp_network::pad_server::structs::PadServerNetwork; use std::sync::Arc; use std::sync::atomic::Ordering::SeqCst; use log::{error, info, trace, warn}; @@ -9,12 +6,15 @@ use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::TcpStream; use tokio::spawn; use nogamepads::entry_mutex; -use crate::data::message::enums::{ControlMessage, GameMessage}; -use crate::data::message::enums::ExitReason::GameOver; -use crate::data::message::enums::GameMessage::{End, LetExit}; +use crate::data::message::message_enums::{ControlMessage, GameMessage}; +use crate::data::message::message_enums::ExitReason::GameOver; +use crate::data::message::message_enums::GameMessage::{End, LetExit}; use crate::data::message::traits::{MessageEncoder, MessageManager}; +use crate::data::player::player_data::Player; use crate::service::service_types::ServiceType; use crate::service::service_types::ServiceType::TCPConnection; +use crate::service::tcp_network::pad_client::pad_client_service::PadClientNetwork; +use crate::service::tcp_network::pad_server::pad_server_service::PadServerNetwork; impl PadServerNetwork { diff --git a/core/src/service/tcp_network/pad_client/implements.rs b/core/src/service/tcp_network/pad_client/implements.rs deleted file mode 100644 index 021b325..0000000 --- a/core/src/service/tcp_network/pad_client/implements.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::net::{IpAddr, SocketAddr}; -use std::sync::{Arc, Mutex}; -use std::sync::atomic::Ordering::SeqCst; -use std::time::Duration; -use log::{error, info, warn}; -use tokio::{join, spawn}; -use tokio::time::sleep; -use nogamepads::entry_mutex; -use crate::data::controller::runtime::structs::ControllerRuntime; -use crate::data::message::enums::ConnectionMessage::{Join, RequestGameInfos}; -use crate::data::message::enums::ConnectionResponseMessage; -use crate::service::service_runner::NoGamepadsService; -use crate::service::service_types::ServiceType; -use crate::service::tcp_network::pad_client::structs::PadClientNetwork; -use crate::service::tcp_network::DEFAULT_PORT; -use crate::service::tcp_network::utils::stream_utils::{read_msg, send_msg}; -use crate::service::tcp_network::utils::tokio_utils::build_tokio_runtime; - -macro_rules! connect_once { - ($addr:expr, |$conn:ident| $code:block) => {{ - use tokio::net::TcpStream; - match TcpStream::connect($addr).await { - Ok(mut $conn) => { - $code - true - }, - Err(e) => { - error!("[TCP Client] [Main] Connection failed {:?}", e); - false - } - } - }} -} - -impl PadClientNetwork { - - pub fn build(runtime: Arc>) -> PadClientNetwork { - entry_mutex!(runtime, |guard| { - guard.service_type = ServiceType::TCPConnection; - }); - - PadClientNetwork { - addr: SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT)), - runtime - } - } - - pub fn bind_addr(&mut self, addr: SocketAddr) -> &mut PadClientNetwork { - self.addr = addr; - self - } - - pub fn bind_ip(&mut self, addr: IpAddr) -> &mut PadClientNetwork { - self.addr.set_ip(addr); - self - } - - pub fn bind_port(&mut self, port: u16) -> &mut PadClientNetwork { - self.addr.set_port(port); - self - } - - pub fn build_entry(self) -> NoGamepadsService { - let arc = Arc::new(self); - - let entry = async move { - // Connection thread: Download the relevant resources, verify connection eligibility, and attempt to join the game. - let connection_thread = spawn({ - let client = Arc::clone(&arc); - async move { - Self::connection_thread(client).await - } - }); - - // Join - let _ = join!(connection_thread); - }; - - Box::pin(entry) - } - - pub fn connect(self) { - let runtime = build_tokio_runtime("padclient_tcp".to_string()); - - info!("[TCP Client] Connecting to {}:{}", self.addr.ip().to_string(), self.addr.port()); - runtime.block_on(self.build_entry()); - } -} - -impl PadClientNetwork { - - async fn connection_thread(self: Arc) { - let mut buffer = [0; 1024]; - - // Requests game infos - if !connect_once!(self.addr, |stream| { - info!("[TCP Client] [Main] Requesting game infos."); - send_msg(&mut stream, RequestGameInfos).await; - let response : ConnectionResponseMessage = read_msg(&mut buffer, &mut stream).await; - match response { - ConnectionResponseMessage::GameInfos(infos) => { - entry_mutex!(self.runtime, |guard| { - guard.game_info = infos; - }); - info!("[TCP Client] [Main] Download game infos successfully."); - } - ConnectionResponseMessage::Err => { - warn!("[TCP Client] [Main] Download game infos failed."); - } - _ => { - warn!("[TCP Client] [Main] Not found game infos."); - } - } - }) { - return; - } - - // TODO :: Download game layouts - - // TODO :: Download skin assets - - // Try to join game - let _ = connect_once!(self.addr, |connection| { - let mut player = None; - entry_mutex!(self.runtime, |guard| { - player = Some(guard.player.clone()); - }); - if player.is_some() { - info!("[TCP Client] [Main] Trying to join game."); - send_msg(&mut connection, Join(player.unwrap())).await; - let response : ConnectionResponseMessage = read_msg(&mut buffer, &mut connection).await; - match response { - ConnectionResponseMessage::Welcome => { - - // Long Connection - info!("[TCP Client] [Main] Welcome"); - spawn(Self::start_long_connection(Arc::clone(&self), connection)); - } - ConnectionResponseMessage::Deny(why) => { - error!("[TCP Client] [Main] Connection denied: {:?}", why); - } - _ => { } - } - } else { - error!("[TCP Client] [Main] No player found."); - return; - } - }); - - loop { - sleep(Duration::from_millis(1000)).await; - entry_mutex!(self.runtime, |guard| { - if guard.close.load(SeqCst) { - break; - } - }) - } - - info!("[TCP Client] [Main] Main thread closed."); - } -} \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_client/mod.rs b/core/src/service/tcp_network/pad_client/mod.rs index 0ff870f..4e9794d 100644 --- a/core/src/service/tcp_network/pad_client/mod.rs +++ b/core/src/service/tcp_network/pad_client/mod.rs @@ -1,2 +1 @@ -pub mod implements; -pub mod structs; \ No newline at end of file +pub mod pad_client_service; \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_client/pad_client_service.rs b/core/src/service/tcp_network/pad_client/pad_client_service.rs new file mode 100644 index 0000000..9b1826b --- /dev/null +++ b/core/src/service/tcp_network/pad_client/pad_client_service.rs @@ -0,0 +1,165 @@ +use std::net::{IpAddr, SocketAddr}; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::Ordering::SeqCst; +use std::time::Duration; +use log::{error, info, warn}; +use tokio::{join, spawn}; +use tokio::time::sleep; +use nogamepads::entry_mutex; +use crate::data::controller::controller_runtime::ControllerRuntime; +use crate::data::message::message_enums::ConnectionMessage::{Join, RequestGameInfos}; +use crate::data::message::message_enums::ConnectionResponseMessage; +use crate::service::service_runner::NoGamepadsService; +use crate::service::service_types::ServiceType; +use crate::service::tcp_network::DEFAULT_PORT; +use crate::service::tcp_network::utils::stream_utils::{read_msg, send_msg}; +use crate::service::tcp_network::utils::tokio_utils::build_tokio_runtime; + +pub struct PadClientNetwork { + pub(crate) addr: SocketAddr, + pub(crate) runtime: Arc> +} + +macro_rules! connect_once { + ($addr:expr, |$conn:ident| $code:block) => {{ + use tokio::net::TcpStream; + match TcpStream::connect($addr).await { + Ok(mut $conn) => { + $code + true + }, + Err(e) => { + error!("[TCP Client] [Main] Connection failed {:?}", e); + false + } + } + }} +} + +impl PadClientNetwork { + + pub fn build(runtime: Arc>) -> PadClientNetwork { + entry_mutex!(runtime, |guard| { + guard.service_type = ServiceType::TCPConnection; + }); + + PadClientNetwork { + addr: SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT)), + runtime + } + } + + pub fn bind_addr(&mut self, addr: SocketAddr) -> &mut PadClientNetwork { + self.addr = addr; + self + } + + pub fn bind_ip(&mut self, addr: IpAddr) -> &mut PadClientNetwork { + self.addr.set_ip(addr); + self + } + + pub fn bind_port(&mut self, port: u16) -> &mut PadClientNetwork { + self.addr.set_port(port); + self + } + + pub fn build_entry(self) -> NoGamepadsService { + let arc = Arc::new(self); + + let entry = async move { + // Connection thread: Download the relevant resources, verify connection eligibility, and attempt to join the game. + let connection_thread = spawn({ + let client = Arc::clone(&arc); + async move { + Self::connection_thread(client).await + } + }); + + // Join + let _ = join!(connection_thread); + }; + + Box::pin(entry) + } + + pub fn connect(self) { + let runtime = build_tokio_runtime("padclient_tcp".to_string()); + + info!("[TCP Client] Connecting to {}:{}", self.addr.ip().to_string(), self.addr.port()); + runtime.block_on(self.build_entry()); + } +} + +impl PadClientNetwork { + + async fn connection_thread(self: Arc) { + let mut buffer = [0; 1024]; + + // Requests game infos + if !connect_once!(self.addr, |stream| { + info!("[TCP Client] [Main] Requesting game infos."); + send_msg(&mut stream, RequestGameInfos).await; + let response : ConnectionResponseMessage = read_msg(&mut buffer, &mut stream).await; + match response { + ConnectionResponseMessage::GameInfos(infos) => { + entry_mutex!(self.runtime, |guard| { + guard.game_info = infos; + }); + info!("[TCP Client] [Main] Download game infos successfully."); + } + ConnectionResponseMessage::Err => { + warn!("[TCP Client] [Main] Download game infos failed."); + } + _ => { + warn!("[TCP Client] [Main] Not found game infos."); + } + } + }) { + return; + } + + // TODO :: Download game layouts + + // TODO :: Download skin assets + + // Try to join game + let _ = connect_once!(self.addr, |connection| { + let mut player = None; + entry_mutex!(self.runtime, |guard| { + player = Some(guard.player.clone()); + }); + if player.is_some() { + info!("[TCP Client] [Main] Trying to join game."); + send_msg(&mut connection, Join(player.unwrap())).await; + let response : ConnectionResponseMessage = read_msg(&mut buffer, &mut connection).await; + match response { + ConnectionResponseMessage::Welcome => { + + // Long Connection + info!("[TCP Client] [Main] Welcome"); + spawn(Self::start_long_connection(Arc::clone(&self), connection)); + } + ConnectionResponseMessage::Deny(why) => { + error!("[TCP Client] [Main] Connection denied: {:?}", why); + } + _ => { } + } + } else { + error!("[TCP Client] [Main] No player found."); + return; + } + }); + + loop { + sleep(Duration::from_millis(1000)).await; + entry_mutex!(self.runtime, |guard| { + if guard.close.load(SeqCst) { + break; + } + }) + } + + info!("[TCP Client] [Main] Main thread closed."); + } +} \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_client/structs.rs b/core/src/service/tcp_network/pad_client/structs.rs deleted file mode 100644 index 10c93c4..0000000 --- a/core/src/service/tcp_network/pad_client/structs.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::net::SocketAddr; -use std::sync::{Arc, Mutex}; -use crate::data::controller::runtime::structs::ControllerRuntime; - -pub struct PadClientNetwork { - pub(crate) addr: SocketAddr, - pub(crate) runtime: Arc> -} \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_server/implements.rs b/core/src/service/tcp_network/pad_server/implements.rs deleted file mode 100644 index d4288bf..0000000 --- a/core/src/service/tcp_network/pad_server/implements.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::net::{IpAddr, SocketAddr}; -use std::sync::{Arc, Mutex}; -use std::sync::atomic::Ordering::SeqCst; -use std::time::Duration; -use log::{error, info, trace, warn}; -use tokio::{join, select, spawn}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::watch::{channel}; -use tokio::time::sleep; -use nogamepads::entry_mutex; -use crate::data::game::runtime::structs::GameRuntime; -use crate::data::message::enums::ConnectionMessage; -use crate::data::message::enums::ConnectionMessage::{Join, RequestGameInfos, RequestLayoutConfigure, RequestSkinPackage, Ready}; -use crate::data::message::enums::ConnectionResponseMessage::{Deny, GameInfos, Welcome}; -use crate::service::service_runner::NoGamepadsService; -use crate::service::tcp_network::DEFAULT_PORT; -use crate::service::tcp_network::pad_server::structs::PadServerNetwork; -use crate::service::tcp_network::utils::stream_utils::{get_target_address, read_msg, send_msg}; -use crate::service::tcp_network::utils::tokio_utils::build_tokio_runtime; - -impl PadServerNetwork { - - pub fn build(runtime: Arc>) -> PadServerNetwork { - let (close_tx, close_rx) = channel(false); - PadServerNetwork { - addr: SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT)), - runtime, - close_tx, - close_rx - } - } - - pub fn bind_ip(&mut self, ip: IpAddr) -> &mut PadServerNetwork { - self.addr.set_ip(ip); - self - } - - pub fn bind_port(&mut self, port: u16) -> &mut PadServerNetwork { - self.addr.set_port(port); - self - } - - pub fn build_entry(self) -> NoGamepadsService { - let arc = Arc::new(self); - - let entry = async move { - // Main thread: Used to handle connection requests, data requests, and transfer skin assets - let main_thread = spawn({ - let server = Arc::clone(&arc); - async move { - Self::main_thread(server).await - } - }); - - let close_checker = { - let server = Arc::clone(&arc); - async move { - Self::close_checker(server).await - } - }; - - // Join - let _ = join!(close_checker, main_thread); - }; - - Box::pin(entry) - } - - pub fn listening_block_on(self) { - let runtime = build_tokio_runtime("padserver_tcp".to_string()); - - info!("[TCP Server] Server start."); - runtime.block_on(self.build_entry()); - info!("[TCP Server] Finished."); - } -} - -impl PadServerNetwork { - - async fn main_thread(self: Arc) { - - info!("[TCP Server] [Main] Server listening at {}", self.addr.to_string()); - - let listener = TcpListener::bind(self.addr).await; - if listener.is_err() { - error!("[TCP Server] [Main] Failed to bind to {}", self.addr.to_string()); - return; - } - - let listener = listener.unwrap(); - info!("[TCP Server] [Main] Listener created, start listening."); - - let mut local_close_rx = self.close_rx.clone(); - - loop { - select! { - _ = local_close_rx.changed() => { - if *local_close_rx.borrow() { - break; - } - } - - accept = listener.accept() => { - match accept { - Ok((stream, _)) => { - spawn(Self::process_connection(Arc::clone(&self), stream)); - } - Err(error) => { - warn!("[TCP Server] [Main] Failed to accept TCP connections: {}", error); - } - } - } - } - } - - info!("[TCP Server] [Main] Main thread closed."); - } - - async fn process_connection(self: Arc, mut stream: TcpStream) { - let mut buffer = [0; 1024]; - let message: ConnectionMessage = read_msg(&mut buffer, &mut stream).await; - let from_address = get_target_address(&stream); - - match message { - - Join(player) => { - trace!("[TCP Server] [Main] Trying to join Player \"{}\"", &player.account.id); - let mut result = Ok(()); - entry_mutex!(self.runtime, |guard| { - match guard.try_join_player(player.clone()) { - Ok(_) => { result = Ok(()); } - Err(why) => { result = Err(why); } - } - }); - if result.is_err() { - let fail_message = result.unwrap_err(); - error!("[TCP Server] [Main] Player join failed: {:?}", &fail_message); - send_msg(&mut stream, Deny(fail_message)).await; - } else { - - // Long Connection - info!("[TCP Server] [Main] Player joined, begin long connection."); - send_msg(&mut stream, Welcome).await; - spawn(Self::start_long_connection(Arc::clone(&self), player, stream)); - } - } - - RequestGameInfos => { - info!("[TCP Server] [Main] Client({}) requests game infos.", from_address); - let mut info = Default::default(); - entry_mutex!(self.runtime, |guard| { - info = guard.info.clone(); - }); - send_msg(&mut stream, GameInfos(info)).await; - info!("[TCP Server] [Main] Game infos sent."); - } - - RequestLayoutConfigure => { - info!("[TCP Server] [Main] Client({}) requests layout configures.", from_address); - } - - RequestSkinPackage => { - info!("[TCP Server] [Main] Client({}) requests to download skin package.", from_address); - } - - Ready => { - info!("[TCP Server] [Main] Client({}) is ready!", from_address); - warn!("[TCP Server] [Main] But I don't know who he is.....") - } - - _ => { } - } - } - - async fn close_checker(self: Arc) { - loop { - sleep(Duration::from_millis(1000)).await; - entry_mutex!(self.runtime, |guard| { - if guard.data.close.load(SeqCst) { - let _ = self.close_tx.send(true); - break; - } - }) - } - } -} \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_server/mod.rs b/core/src/service/tcp_network/pad_server/mod.rs index b37b320..43d81a9 100644 --- a/core/src/service/tcp_network/pad_server/mod.rs +++ b/core/src/service/tcp_network/pad_server/mod.rs @@ -1,2 +1 @@ -pub mod implements; -pub mod structs; +pub mod pad_server_service; \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_server/pad_server_service.rs b/core/src/service/tcp_network/pad_server/pad_server_service.rs new file mode 100644 index 0000000..1b665a0 --- /dev/null +++ b/core/src/service/tcp_network/pad_server/pad_server_service.rs @@ -0,0 +1,193 @@ +use std::net::{IpAddr, SocketAddr}; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::Ordering::SeqCst; +use std::time::Duration; +use log::{error, info, trace, warn}; +use tokio::{join, select, spawn}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::watch::{channel, Receiver, Sender}; +use tokio::time::sleep; +use nogamepads::entry_mutex; +use crate::data::game::game_runtime::GameRuntime; +use crate::data::message::message_enums::ConnectionMessage; +use crate::data::message::message_enums::ConnectionMessage::{Join, RequestGameInfos, RequestLayoutConfigure, RequestSkinPackage, Ready}; +use crate::data::message::message_enums::ConnectionResponseMessage::{Deny, GameInfos, Welcome}; +use crate::service::service_runner::NoGamepadsService; +use crate::service::tcp_network::DEFAULT_PORT; +use crate::service::tcp_network::utils::stream_utils::{get_target_address, read_msg, send_msg}; +use crate::service::tcp_network::utils::tokio_utils::build_tokio_runtime; + +pub struct PadServerNetwork { + pub(crate) addr: SocketAddr, + pub(crate) runtime: Arc>, + + pub(crate) close_tx: Sender, + pub(crate) close_rx: Receiver, +} + +impl PadServerNetwork { + + pub fn build(runtime: Arc>) -> PadServerNetwork { + let (close_tx, close_rx) = channel(false); + PadServerNetwork { + addr: SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT)), + runtime, + close_tx, + close_rx + } + } + + pub fn bind_ip(&mut self, ip: IpAddr) -> &mut PadServerNetwork { + self.addr.set_ip(ip); + self + } + + pub fn bind_port(&mut self, port: u16) -> &mut PadServerNetwork { + self.addr.set_port(port); + self + } + + pub fn build_entry(self) -> NoGamepadsService { + let arc = Arc::new(self); + + let entry = async move { + // Main thread: Used to handle connection requests, data requests, and transfer skin assets + let main_thread = spawn({ + let server = Arc::clone(&arc); + async move { + Self::main_thread(server).await + } + }); + + let close_checker = { + let server = Arc::clone(&arc); + async move { + Self::close_checker(server).await + } + }; + + // Join + let _ = join!(close_checker, main_thread); + }; + + Box::pin(entry) + } + + pub fn listening_block_on(self) { + let runtime = build_tokio_runtime("padserver_tcp".to_string()); + + info!("[TCP Server] Server start."); + runtime.block_on(self.build_entry()); + info!("[TCP Server] Finished."); + } +} + +impl PadServerNetwork { + + async fn main_thread(self: Arc) { + + info!("[TCP Server] [Main] Server listening at {}", self.addr.to_string()); + + let listener = TcpListener::bind(self.addr).await; + if listener.is_err() { + error!("[TCP Server] [Main] Failed to bind to {}", self.addr.to_string()); + return; + } + + let listener = listener.unwrap(); + info!("[TCP Server] [Main] Listener created, start listening."); + + let mut local_close_rx = self.close_rx.clone(); + + loop { + select! { + _ = local_close_rx.changed() => { + if *local_close_rx.borrow() { + break; + } + } + + accept = listener.accept() => { + match accept { + Ok((stream, _)) => { + spawn(Self::process_connection(Arc::clone(&self), stream)); + } + Err(error) => { + warn!("[TCP Server] [Main] Failed to accept TCP connections: {}", error); + } + } + } + } + } + + info!("[TCP Server] [Main] Main thread closed."); + } + + async fn process_connection(self: Arc, mut stream: TcpStream) { + let mut buffer = [0; 1024]; + let message: ConnectionMessage = read_msg(&mut buffer, &mut stream).await; + let from_address = get_target_address(&stream); + + match message { + + Join(player) => { + trace!("[TCP Server] [Main] Trying to join Player \"{}\"", &player.account.id); + let mut result = Ok(()); + entry_mutex!(self.runtime, |guard| { + match guard.try_join_player(player.clone()) { + Ok(_) => { result = Ok(()); } + Err(why) => { result = Err(why); } + } + }); + if result.is_err() { + let fail_message = result.unwrap_err(); + error!("[TCP Server] [Main] Player join failed: {:?}", &fail_message); + send_msg(&mut stream, Deny(fail_message)).await; + } else { + + // Long Connection + info!("[TCP Server] [Main] Player joined, begin long connection."); + send_msg(&mut stream, Welcome).await; + spawn(Self::start_long_connection(Arc::clone(&self), player, stream)); + } + } + + RequestGameInfos => { + info!("[TCP Server] [Main] Client({}) requests game infos.", from_address); + let mut info = Default::default(); + entry_mutex!(self.runtime, |guard| { + info = guard.info.clone(); + }); + send_msg(&mut stream, GameInfos(info)).await; + info!("[TCP Server] [Main] Game infos sent."); + } + + RequestLayoutConfigure => { + info!("[TCP Server] [Main] Client({}) requests layout configures.", from_address); + } + + RequestSkinPackage => { + info!("[TCP Server] [Main] Client({}) requests to download skin package.", from_address); + } + + Ready => { + info!("[TCP Server] [Main] Client({}) is ready!", from_address); + warn!("[TCP Server] [Main] But I don't know who he is.....") + } + + _ => { } + } + } + + async fn close_checker(self: Arc) { + loop { + sleep(Duration::from_millis(1000)).await; + entry_mutex!(self.runtime, |guard| { + if guard.data.close.load(SeqCst) { + let _ = self.close_tx.send(true); + break; + } + }) + } + } +} \ No newline at end of file diff --git a/core/src/service/tcp_network/pad_server/structs.rs b/core/src/service/tcp_network/pad_server/structs.rs deleted file mode 100644 index f46608a..0000000 --- a/core/src/service/tcp_network/pad_server/structs.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::data::game::runtime::structs::GameRuntime; -use std::net::SocketAddr; -use std::sync::{Arc, Mutex}; -use tokio::sync::watch::{Receiver, Sender}; - -pub struct PadServerNetwork { - pub(crate) addr: SocketAddr, - pub(crate) runtime: Arc>, - - pub(crate) close_tx: Sender, - pub(crate) close_rx: Receiver, -} \ No newline at end of file -- cgit