diff options
| author | 1992414357@qq.com <1992414357@qq.com> | 2025-04-21 06:25:54 +0800 |
|---|---|---|
| committer | 1992414357@qq.com <1992414357@qq.com> | 2025-04-21 06:25:54 +0800 |
| commit | 50ea9869616846e5a0448ead8272c397080f36d8 (patch) | |
| tree | 5f2ec829ecf48441a6d33f4160909dc02ddf83bf | |
| parent | d72d700c84794ca420035ceb9c294563f3f9c048 (diff) | |
.
26 files changed, 470 insertions, 181 deletions
diff --git a/.idea/runConfigurations/Test_Message_Encoder.xml b/.idea/runConfigurations/Test_Message_Encoder.xml new file mode 100644 index 0000000..eb9d4d6 --- /dev/null +++ b/.idea/runConfigurations/Test_Message_Encoder.xml @@ -0,0 +1,20 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="Test Message Encoder" type="CargoCommandRunConfiguration" factoryName="Cargo Command" folderName="Test"> + <option name="buildProfileId" value="test" /> + <option name="command" value="test --package there-is-nopads-core --lib message_encoder::encoder_tests::test_encode_decode -- --exact" /> + <option name="workingDirectory" value="file://$PROJECT_DIR$/core" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> +</component>
\ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 8f955fe..47350f4 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,28 +4,63 @@ <option name="autoReloadType" value="ALL" /> </component> <component name="CargoProjects"> - <cargoProject FILE="$PROJECT_DIR$/core/server_test/Cargo.toml" /> <cargoProject FILE="$PROJECT_DIR$/client/Cargo.toml" /> <cargoProject FILE="$PROJECT_DIR$/core/Cargo.toml" /> + <cargoProject FILE="$PROJECT_DIR$/core/test/Cargo.toml" /> </component> <component name="ChangeListManager"> <list default="true" id="43098f42-7a3b-4fc2-ad78-a08c96f7f5db" name="Changes" comment="修改作者所有权"> - <change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.gitignore" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/.init/generate_docs.bat" beforeDir="false" afterPath="$PROJECT_DIR$/.init/generate_docs.bat" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/client/client.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/client/client_event.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/client/mod.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/message_encoder.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/messages.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/player/mod.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/server/mod.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/core/src/server/server_event.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/doc/Client - 客户端.md" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/doc/Player - 玩家.md" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/doc/STRUCT.canvas" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/doc/Server - 服务器.md" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.obsidian/appearance.json" beforeDir="false" afterPath="$PROJECT_DIR$/.obsidian/appearance.json" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.obsidian/core-plugins.json" beforeDir="false" afterPath="$PROJECT_DIR$/.obsidian/core-plugins.json" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.obsidian/workspace.json" beforeDir="false" afterPath="$PROJECT_DIR$/.obsidian/workspace.json" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/core/Cargo.toml" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/constants.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/constants.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/lib.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/pads_player.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/player/player_info.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/pads_player_commands.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/client/client_commands.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/pads_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/server/server.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/src/pas_server_commands.rs" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/server/server_commands.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/core/test/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/core/test/Cargo.toml" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/doc/基础功能列表.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/基础功能列表.md" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/doc/项目结构.canvas" beforeDir="false" /> </list> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="LAST_RESOLUTION" value="IGNORE" /> </component> - <component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:dev" /> + <component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:test" /> + <component name="FileTemplateManagerImpl"> + <option name="RECENT_TEMPLATES"> + <list> + <option value="Rust File" /> + </list> + </option> + </component> + <component name="Git.Settings"> + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> + </component> <component name="MacroExpansionManager"> <option name="directoryName" value="EK7IzXE2" /> </component> - <component name="ProjectColorInfo"><![CDATA[{ - "customColor": "", - "associatedIndex": 1 -}]]></component> + <component name="ProjectColorInfo">{ + "customColor": "", + "associatedIndex": 1 +}</component> <component name="ProjectId" id="2vby3gKCsFT9WfRIRxaNtOUu2Bx" /> <component name="ProjectViewState"> <option name="hideEmptyMiddlePackages" value="true" /> @@ -36,10 +71,14 @@ "Attach to Node.js/Chrome.Unnamed.executor": "Debug", "Cargo.Run phantomlink-gamepad-client.executor": "Run", "Cargo.Run phantomlink-server-test.executor": "Run", + "Cargo.Test Message Encoder.executor": "Run", + "Cargo.Test encoder_tests.executor": "Run", + "Cargo.Test message_encoder::encoder_tests::test_encode_decode.executor": "Run", "RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.git.unshallow": "true", "RunOnceActivity.rust.reset.selective.auto.import": "true", "Shell Script.Shell Generate Documents.executor": "Run", + "git-widget-placeholder": "main", "node.js.detected.package.eslint": "true", "node.js.detected.package.tslint": "true", "node.js.selected.package.eslint": "(autodetect)", @@ -51,17 +90,40 @@ "vue.rearranger.settings.migration": "true" } }]]></component> - <component name="RunManager" selected="Shell Script.Shell Initialize Project"> + <component name="RunManager" selected="Cargo.Test encoder_tests"> + <configuration name="Test encoder_tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> + <option name="command" value="test --package there-is-nopads-core --lib message_encoder::encoder_tests" /> + <option name="workingDirectory" value="file://$PROJECT_DIR$/core" /> + <envs /> + <option name="emulateTerminal" value="true" /> + <option name="channel" value="DEFAULT" /> + <option name="requiredFeatures" value="true" /> + <option name="allFeatures" value="false" /> + <option name="withSudo" value="false" /> + <option name="buildTarget" value="REMOTE" /> + <option name="backtrace" value="SHORT" /> + <option name="isRedirectInput" value="false" /> + <option name="redirectInputPath" value="" /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> <list> - <item itemvalue="Cargo.Run Server Test" /> - <item itemvalue="Cargo.Run Client" /> - <item itemvalue="Shell Script.Shell Initialize Project" /> - <item itemvalue="Shell Script.Shell Generate Documents" /> + <item itemvalue="Cargo.Run Console Client" /> + <item itemvalue="Cargo.Run Console Server" /> + <item itemvalue="Cargo.Run Test" /> + <item itemvalue="Cargo.Test Message Encoder" /> + <item itemvalue="Cargo.Run Gamepad Client" /> + <item itemvalue="Cargo.Test encoder_tests" /> + <item itemvalue="Shell Script.Generate Documents (Linux)" /> + <item itemvalue="Shell Script.Initialize (Linux)" /> + <item itemvalue="Shell Script.Generate Documents (Windows)" /> + <item itemvalue="Shell Script.Initialize (Windows)" /> </list> <recent_temporary> <list> - <item itemvalue="Cargo.Run Client" /> - <item itemvalue="Cargo.Run Server Test" /> + <item itemvalue="Cargo.Test encoder_tests" /> + <item itemvalue="Cargo.Test Message Encoder" /> </list> </recent_temporary> </component> @@ -77,6 +139,7 @@ <option name="presentableId" value="Default" /> <updated>1744427871470</updated> <workItem from="1744427873184" duration="1209000" /> + <workItem from="1745180836475" duration="6545000" /> </task> <task id="LOCAL-00001" summary="修改作者所有权"> <option name="closed" value="true" /> diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json index 9e26dfe..e026aad 100644 --- a/.obsidian/appearance.json +++ b/.obsidian/appearance.json @@ -1 +1,4 @@ -{}
\ No newline at end of file +{ + "accentColor": "#ff1a1a", + "nativeMenus": false +}
\ No newline at end of file diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json index 436f43c..c89a841 100644 --- a/.obsidian/core-plugins.json +++ b/.obsidian/core-plugins.json @@ -26,5 +26,6 @@ "workspaces": false, "file-recovery": true, "publish": false, - "sync": false + "sync": false, + "webviewer": false }
\ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 45d7d33..8889e1a 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -11,6 +11,23 @@ "id": "8fab3a873edfc7f3", "type": "leaf", "state": { + "type": "canvas", + "state": { + "file": "doc/STRUCT.canvas", + "viewState": { + "x": 569.6581039476258, + "y": -2212.1264050437776, + "zoom": -1.5882018370782922 + } + }, + "icon": "lucide-layout-dashboard", + "title": "STRUCT" + } + }, + { + "id": "f33bc2925c565d58", + "type": "leaf", + "state": { "type": "markdown", "state": { "file": "doc/基础功能列表.md", @@ -40,7 +57,8 @@ "state": { "type": "file-explorer", "state": { - "sortOrder": "alphabetical" + "sortOrder": "alphabetical", + "autoReveal": false }, "icon": "lucide-folder-closed", "title": "文件列表" @@ -164,19 +182,24 @@ }, "active": "8fab3a873edfc7f3", "lastOpenFiles": [ - "doc/项目结构.canvas", - "doc/基础功能列表.md", - "core/src/pads_server.rs~", - "core/test/desktop.ini", - "core/desktop.ini~", - "core/src/pads_player_commands.rs~", - "core/src/pads_player.rs~", + "core/src/message_encoder.rs~", + "core/src/player/player_info.rs~", + "core/src/messages.rs~", + "core/src/constants.rs~", + "core/src/lib.rs~", "core/Cargo.toml~", - "core/test/src/bin/console_server.rs~", - "core/test/src/bin/console_client.rs", - "core/test/src/bin/console_server.rs", - "core/test/src/main.rs", + "core/src/client/client.rs~", + "core/src/message_encoder.rs", + "core/src/client/client_event.rs~", + "core/src/server/server.rs~", + "core/src/server/server_event.rs~", + "doc/Client - 客户端.md", + "doc/Server - 服务器.md", + "doc/STRUCT.canvas", + "doc/基础功能列表.md", + "doc/Player - 玩家.md", "README.md", + "doc/未命名.md", "Client/Art/Pad.png", "Client/Art/config/skin.png", "LICENSE.md", @@ -1,2 +1,2 @@ -# PhantomLink-Gamepad +# There Is No Pads A mobile device control solution for local multiplayer gaming, featuring an in-game interface, controller client, and support for universal devices. diff --git a/core/Cargo.toml b/core/Cargo.toml index df61b91..309ef64 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,6 +13,6 @@ edition = "2024" [dependencies] sha1 = "0.10.6" serde = { version = "1.0.219", features = ["derive"] } -bincode = "2.0.1" +bincode = { version = "2.0.1", features = ["serde"]} tokio = { version = "1.0", features = ["full"] } hex = "0.4.3"
\ No newline at end of file diff --git a/core/src/client/client.rs b/core/src/client/client.rs new file mode 100644 index 0000000..c11bb61 --- /dev/null +++ b/core/src/client/client.rs @@ -0,0 +1,29 @@ +use crate::constants::DEFAULT_PORT; +use crate::player::player_info::PlayerInfo; +use std::net::SocketAddr; + +/// 玩家客户端 +pub struct PadClient { + player_info: PlayerInfo, + remote_addr: SocketAddr, + receive_skin_package : bool +} + +impl Default for PadClient { + + fn default() -> Self { + PadClient { + player_info: PlayerInfo::default(), + remote_addr: SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT)), + receive_skin_package: true + } + } +} + +impl PadClient { + + /// # 连接到服务器 + pub fn connect_server(&self) { + + } +}
\ No newline at end of file diff --git a/core/src/pads_player_commands.rs b/core/src/client/client_commands.rs index 276805f..f674f6c 100644 --- a/core/src/pads_player_commands.rs +++ b/core/src/client/client_commands.rs @@ -1,4 +1,5 @@ -use crate::pads_player::{PadClient, PlayerMessage}; +use crate::client::client::PadClient; +use crate::messages::PlayerMessage; impl PadClient { diff --git a/core/src/client/client_event.rs b/core/src/client/client_event.rs new file mode 100644 index 0000000..ac52e53 --- /dev/null +++ b/core/src/client/client_event.rs @@ -0,0 +1,26 @@ +use crate::messages::ServerMessage; + +/// 玩家客户端事件 +pub trait PadClientEvent { + + /// 加入游戏 + fn joined_game(); + + /// 加入游戏失败 + fn joined_game_failed(); + + /// 退出游戏 + fn left_game(); + + /// 被踢出游戏 + fn kicked(); + + /// 被该游戏封禁(若游戏未保存名单,则游戏重启前无法进入) + fn banned(); + + /// 掉线 + fn disconnected(); + + /// 接收到消息 + fn msg_received(msg: ServerMessage); +}
\ No newline at end of file diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs new file mode 100644 index 0000000..0cb7dd2 --- /dev/null +++ b/core/src/client/mod.rs @@ -0,0 +1,3 @@ +pub mod client_commands; +pub mod client; +pub mod client_event;
\ No newline at end of file diff --git a/core/src/constants.rs b/core/src/constants.rs index a66f79f..6257c9d 100644 --- a/core/src/constants.rs +++ b/core/src/constants.rs @@ -1 +1,8 @@ -pub const SALT : &str = ":)";
\ No newline at end of file +use bincode::config; +use bincode::config::Configuration; + +pub const SALT : &str = ":)"; + +pub const DEFAULT_PORT : u16 = 8916; + +pub const BINCODE_CONFIG : Configuration = config::standard();
\ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index 056b046..d8b1df8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,5 +1,9 @@ -pub mod pads_server; -pub mod pads_player; +// Public +pub mod client; +pub mod player; +pub mod server; +pub mod messages; + +// Private mod constants; -mod pas_server_commands; -mod pads_player_commands;
\ No newline at end of file +mod message_encoder; diff --git a/core/src/message_encoder.rs b/core/src/message_encoder.rs new file mode 100644 index 0000000..a8575d0 --- /dev/null +++ b/core/src/message_encoder.rs @@ -0,0 +1,71 @@ +use std::io::{Read, Write}; +use bincode::error::{DecodeError, EncodeError}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use crate::messages::{ClientMessage, PlayerMessage, ServerCallbackMessage, ServerMessage}; + +pub trait MessageEncoder: Serialize + DeserializeOwned { + fn encode(&self) -> Result<usize, EncodeError>; + fn decode(encode: usize) -> Result<Self, DecodeError> + where + Self: Sized; +} + +impl MessageEncoder for PlayerMessage { + fn encode(&self) -> Result<usize, EncodeError> { + bincode::serde::encode_into_std_write(self, &mut std::io::sink(), bincode::config::standard()) + } + + fn decode(encode: &usize) -> Result<Self, DecodeError> { + bincode::serde::decode_from_std_read(&mut &encode[..], bincode::config::standard()) + } +} + +impl MessageEncoder for ServerMessage { + fn encode(&self) -> Result<usize, EncodeError> { + bincode::serde::encode_into_std_write(self, &mut std::io::sink(), bincode::config::standard()) + } + + fn decode(encode: usize) -> Result<Self, DecodeError> { + bincode::serde::decode_from_std_read(&mut &encode[..], bincode::config::standard()) + } +} + +impl MessageEncoder for ClientMessage { + fn encode(&self) -> Result<usize, EncodeError> { + bincode::serde::encode_into_std_write(self, &mut std::io::sink(), bincode::config::standard()) + } + + fn decode(encode: usize) -> Result<Self, DecodeError> { + bincode::serde::decode_from_std_read(&mut &encode[..], bincode::config::standard()) + } +} + +impl MessageEncoder for ServerCallbackMessage { + fn encode(&self) -> Result<usize, EncodeError> { + bincode::serde::encode_into_std_write(self, &mut std::io::sink(), bincode::config::standard()) + } + + fn decode(encode: usize) -> Result<Self, DecodeError> { + bincode::serde::decode_from_std_read(&mut &encode[..], bincode::config::standard()) + } +} + +#[cfg(test)] +mod encoder_tests { + use crate::message_encoder::MessageEncoder; + use crate::messages::PlayerMessage; + + + #[test] + fn test_encode_decode() { + + let msg: String = String::from("Hello, World!"); + + let msg_player = PlayerMessage::Message(msg); + let msg_player_encoded = (&msg_player).encode().expect("Encode failed"); + let msg_player_decoded = PlayerMessage::decode(msg_player_encoded).expect("Decode failed"); + + assert!(msg_player == msg_player_decoded) + } +}
\ No newline at end of file diff --git a/core/src/messages.rs b/core/src/messages.rs new file mode 100644 index 0000000..2ae8328 --- /dev/null +++ b/core/src/messages.rs @@ -0,0 +1,78 @@ +use std::time::Duration; +use serde::{Deserialize, Serialize}; +use crate::player::player_info::PlayerInfo; + +/// # 玩家消息 +/// 玩家 --> 服务器 +#[derive(Serialize, Deserialize, PartialEq)] +pub enum PlayerMessage { + /// 消息:发送字符串,服务端无需回复 + Message(String), + + /// 命令:发送字符串,服务器必须回复 + Command(String), + + /// 按钮按下:发送 u8 作为按键代号 + ButtonPressed(u8), + + /// 按钮抬起:发送 u8 作为按键代号 + ButtonReleased(u8), + + /// 轴向:发送 u8 作为轴向代号,两个 f64 分别代表 x 和 y 方向的轴向 + /// u8:轴向代号 + /// f64:X 轴向值 (0 - 1) + /// f64:Y 轴向值 (0 - 1) + Axis(u8, (f64, f64)) +} + +/// # 服务器消息 +/// 服务器 --> 玩家 +#[derive(Serialize, Deserialize, PartialEq)] +pub enum ServerMessage { + + /// 命令:发送字符串,客户端处理 + Command(String), + + /// 动画:发送字符串,客户端触发对应名称动画 + Animation(String), + + /// 震动:发送 Duration 作为时长,f64 作为震动强度 (0 - 1) + Shock(Duration, f64), + + /// 踢出:发送踢出指令,以便让客户端明白自己的掉线是因为被踢出 + /// “杀人还要诛心!!!好可怕啊!” + Kick +} + +/// # 客户端消息 +/// 客户端 --> 服务器 +#[derive(Serialize, Deserialize, PartialEq)] +pub enum ClientMessage { + + /// 连接请求,将和服务端建立连接,并通知服务器将要建立连接的玩家 + ConnectRequest(PlayerInfo), + + /// 下载布局请求 + RequestLayout, + + /// 下载皮肤请求 + RequestSkin, + + /// 客户端准备就绪 + Ready +} + +/// # 服务器回调消息 +/// 服务器 --> 客户端 +#[derive(Serialize, Deserialize, PartialEq)] +pub enum ServerCallbackMessage { + + /// 拒绝:发送消息已通知客户端请求被拒绝 + Refuse, + + /// 接收:发送消息已通知客户端请求被接受,以继续操作 + Accept, + + /// 欢迎:客户端准备完成后,服务端发送 Welcome 以允许客户端以玩家加入游戏 + Welcome +}
\ No newline at end of file diff --git a/core/src/player/mod.rs b/core/src/player/mod.rs new file mode 100644 index 0000000..fcb7558 --- /dev/null +++ b/core/src/player/mod.rs @@ -0,0 +1 @@ +pub mod player_info;
\ No newline at end of file diff --git a/core/src/pads_player.rs b/core/src/player/player_info.rs index eb9c661..6412b66 100644 --- a/core/src/pads_player.rs +++ b/core/src/player/player_info.rs @@ -1,54 +1,10 @@ -use std::net::SocketAddr; use hex::encode; use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use crate::constants::SALT; -use crate::pads_server::ServerMessage; - -/// 玩家客户端 -pub struct PadClient { - player_info: PlayerInfo, - remote_addr: SocketAddr, - receive_skin_package : bool -} - -impl Default for PadClient { - - fn default() -> Self { - PadClient { - player_info: PlayerInfo::default(), - remote_addr: SocketAddr::from(([127, 0, 0, 1], 8916)), - receive_skin_package: true - } - } -} - -/// 玩家客户端事件 -pub trait PadClientEvent { - - /// 加入游戏 - fn joined_game(); - - /// 加入游戏失败 - fn joined_game_failed(); - - /// 退出游戏 - fn left_game(); - - /// 被踢出游戏 - fn kicked(); - - /// 被该游戏封禁(若游戏未保存名单,则游戏重启前无法进入) - fn banned(); - - /// 掉线 - fn disconnected(); - - /// 接收到消息 - fn msg_received(msg: ServerMessage); -} /// 玩家信息 +#[derive(Serialize, Deserialize, PartialEq)] pub struct PlayerInfo { pub display_name: String, pub name_hash: String, @@ -56,7 +12,7 @@ pub struct PlayerInfo { // 颜色 HSV pub color_hue: i32, // 0 - 360 pub color_saturation: f64, // 0 - 1 - pub color_value: f64 // 0 - 1 + pub color_value: f64, // 0 - 1 } impl Default for PlayerInfo { @@ -72,16 +28,6 @@ impl Default for PlayerInfo { } } -/// 玩家消息 -#[derive(Serialize, Deserialize, PartialEq)] -pub enum PlayerMessage { - Message(String), - Command(String), - ButtonPressed(u8), - ButtonReleased(u8), - Axis(u8, (f64, f64)) -} - impl PlayerInfo { /// # 配置玩家 ID 和 名称 diff --git a/core/src/server/mod.rs b/core/src/server/mod.rs new file mode 100644 index 0000000..d7553a8 --- /dev/null +++ b/core/src/server/mod.rs @@ -0,0 +1,3 @@ +pub mod server; +pub mod server_commands; +pub mod server_event;
\ No newline at end of file diff --git a/core/src/pads_server.rs b/core/src/server/server.rs index d10a88b..c71892c 100644 --- a/core/src/pads_server.rs +++ b/core/src/server/server.rs @@ -1,7 +1,6 @@ +use crate::constants::DEFAULT_PORT; +use crate::server::server_event::PadServerEvent; use std::net::{SocketAddr, UdpSocket}; -use std::time::Duration; -use bincode::Encode; -use crate::pads_player::{PlayerInfo, PlayerMessage}; /// # 监听服务器 /// 监听服务器用于连接玩家设备,并接收玩家的按键输入 @@ -17,7 +16,7 @@ impl Default for PadServer { fn default() -> Self { PadServer { server_name: String::from("This Is No Pads"), - port: 8916 + port: DEFAULT_PORT // TODO :: 补充布局文件 } } @@ -54,46 +53,4 @@ impl PadServer { pub fn stop_listening(&self) { } -} - -/// # 监听服务器事件 -/// 服务器运行过程中的信息将会发送到该 Event Trait -pub trait PadServerEvent { - - /// 服务器启动时 - fn server_start(); - - /// 服务器关闭时 - fn server_shutdown(); - - /// 玩家加入请求 - /// info : 玩家信息 - /// return -> 是否允许加入服务器 - fn join_request_received(info: PlayerInfo) -> bool; - - /// 玩家加入游戏 - /// info : 玩家信息 - fn player_joined(info: PlayerInfo); - - /// 玩家离开游戏 - /// info : 玩家信息 - fn player_left(info: PlayerInfo); - - /// 玩家断开连接 - /// info : 玩家信息 - fn player_disconnected(info: PlayerInfo); - - /// 接收到消息 - /// info : 玩家信息 - /// msg : 接受的消息 - fn msg_received(info: PlayerInfo, msg: PlayerMessage); -} - -/// # 监听服务器消息 -/// 监听服务器用于向玩家发送指令 -pub enum ServerMessage { - Command(String), - Animation(String), - Shock(Duration, f64), - Kick }
\ No newline at end of file diff --git a/core/src/pas_server_commands.rs b/core/src/server/server_commands.rs index 8d15261..dcf40a3 100644 --- a/core/src/pas_server_commands.rs +++ b/core/src/server/server_commands.rs @@ -1,5 +1,6 @@ use std::time::Duration; -use crate::pads_server::{PadServer, ServerMessage}; +use crate::messages::ServerMessage; +use crate::server::server::PadServer; impl PadServer { diff --git a/core/src/server/server_event.rs b/core/src/server/server_event.rs new file mode 100644 index 0000000..f63df5f --- /dev/null +++ b/core/src/server/server_event.rs @@ -0,0 +1,36 @@ +use crate::messages::PlayerMessage; +use crate::player::player_info::PlayerInfo; + +/// # 监听服务器事件 +/// 服务器运行过程中的信息将会发送到该 Event Trait +pub trait PadServerEvent { + + /// 服务器启动时 + fn server_start(); + + /// 服务器关闭时 + fn server_shutdown(); + + /// 玩家加入请求 + /// info : 玩家信息 + /// return -> 是否允许加入服务器 + fn join_request_received(info: PlayerInfo) -> bool; + + /// 玩家加入游戏 + /// info : 玩家信息 + fn player_joined(info: PlayerInfo); + + /// 玩家离开游戏 + /// info : 玩家信息 + fn player_left(info: PlayerInfo); + + /// 玩家断开连接 + /// info : 玩家信息 + fn player_disconnected(info: PlayerInfo); + + /// 接收到消息 + /// info : 玩家信息 + /// msg : 接受的消息 + fn msg_received(info: PlayerInfo, msg: PlayerMessage); +} + diff --git a/core/test/Cargo.toml b/core/test/Cargo.toml index d78fa4f..0031ae8 100644 --- a/core/test/Cargo.toml +++ b/core/test/Cargo.toml @@ -5,6 +5,4 @@ version = "0.1.0" edition = "2024" [dependencies] -there-is-nopads-core = { path = "../../core" } - -# Not A Controller
\ No newline at end of file +there-is-nopads-core = { path = "../../core" }
\ No newline at end of file diff --git a/doc/Client - 客户端.md b/doc/Client - 客户端.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/Client - 客户端.md diff --git a/doc/Player - 玩家.md b/doc/Player - 玩家.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/Player - 玩家.md diff --git a/doc/项目结构.canvas b/doc/STRUCT.canvas index dff1b47..583a86d 100644 --- a/doc/项目结构.canvas +++ b/doc/STRUCT.canvas @@ -1,42 +1,53 @@ { "nodes":[ - {"id":"693c49f59b413ba2","type":"text","text":"**CORE - 内核**","x":-108,"y":-220,"width":250,"height":60}, - {"id":"274e9bdc3ed07a30","type":"text","text":"**ENGINE BINDING - 引擎绑定**","x":-125,"y":-74,"width":284,"height":54}, - {"id":"13029209c27cc6dc","type":"text","text":"**QR CODE - 二维码**","x":-710,"y":-50,"width":250,"height":60}, - {"id":"970b836287034716","type":"text","text":"**CLIENT - 客户端**","x":-710,"y":110,"width":250,"height":50}, - {"id":"ae51ac1a55eb30ff","type":"text","text":"**PLAYER - 玩家**","x":-1120,"y":30,"width":250,"height":60}, - {"id":"6251c445b778d1a3","type":"text","text":"**支持的游戏**","x":-108,"y":40,"width":250,"height":50}, - {"id":"a3d79f917242ddd3","type":"text","text":"**不支持的游戏**","x":-108,"y":180,"width":250,"height":50}, - {"id":"0bbe75b82333d2f1","type":"text","text":"1. 自定义控制器界面\n2. 自定义控制器逻辑\n3. 自定义玩家信息","x":-870,"y":340,"width":250,"height":94}, - {"id":"09f45096019f8e93","type":"text","text":"核心","x":-898,"y":-791,"width":250,"height":60}, - {"id":"c19d7f3e2e7c9dde","type":"text","text":"皮肤制作器","x":-501,"y":-955,"width":250,"height":60}, - {"id":"4138f0aad76fc8c8","type":"text","text":"客户端","x":-560,"y":-786,"width":250,"height":50}, - {"id":"f18abb5436784cee","type":"text","text":"软件操控端","x":-140,"y":-791,"width":250,"height":60}, - {"id":"2b9761ae59d3c1c1","type":"text","text":"服务端","x":-1260,"y":-791,"width":250,"height":60}, - {"id":"933e04c72421cba8","type":"text","text":"游戏实现框架","x":-1620,"y":-791,"width":250,"height":60}, - {"id":"f16c9046ce342942","type":"text","text":"游戏","x":-1620,"y":-600,"width":250,"height":60}, - {"id":"61791938e8aa672c","type":"text","text":"从服务端下载布局文件","x":-648,"y":-2009,"width":240,"height":60}, - {"id":"e8680ff676132dd1","type":"text","text":"应用布局文件","x":-283,"y":-2009,"width":166,"height":60}, - {"id":"a0b3d2f2fba623d9","type":"text","text":"失败","x":-268,"y":-2180,"width":96,"height":60}, - {"id":"aa81015dae795f02","type":"text","text":"重试","x":192,"y":-2180,"width":240,"height":60}, - {"id":"a7eb26e63ce25f28","type":"text","text":"从服务端下载皮肤文件","x":192,"y":-2009,"width":240,"height":60}, - {"id":"f4c69b65009040c9","type":"text","text":"向服务端发送加入信息","x":1232,"y":-2009,"width":250,"height":60}, - {"id":"4ec22f6e59809493","type":"text","text":"重试","x":1232,"y":-2180,"width":250,"height":60}, - {"id":"ee471e71ef3e4254","type":"text","text":"应用皮肤","x":732,"y":-2009,"width":250,"height":60}, - {"id":"3d810b8ede0e7390","type":"text","text":"启用无皮肤模式","x":732,"y":-1840,"width":250,"height":60}, - {"id":"e9902af5372d0567","type":"text","text":"客户端","x":-1633,"y":-2009,"width":250,"height":60,"color":"3"}, - {"id":"b4c5ae3611026103","type":"text","text":"连接到服务端","x":-1193,"y":-2009,"width":242,"height":60}, - {"id":"b25707346da6f026","type":"text","text":"服务端 入口服务","x":-422,"y":-1340,"width":250,"height":60}, - {"id":"d149eb724bb75e1e","type":"text","text":"接收布局文件申请","x":-648,"y":-1740,"width":240,"height":60}, - {"id":"87d43cf10f79b197","type":"text","text":"接收连接申请","x":-1193,"y":-1740,"width":242,"height":60}, - {"id":"439f99fed6b0e32d","type":"text","text":"断开连接","x":-1508,"y":-1740,"width":164,"height":60}, - {"id":"3d7d1730cb96f756","type":"text","text":"失败","x":-1193,"y":-2180,"width":242,"height":60}, - {"id":"8b783d24effb4218","type":"text","text":"接收皮肤文件申请","x":192,"y":-1740,"width":250,"height":60}, - {"id":"5d68be252ba5db2d","type":"text","text":"接收加入游戏信息","x":1232,"y":-1740,"width":250,"height":60}, - {"id":"7faa97ecc512481b","type":"text","text":"创建玩家实例,为该玩家启用专用线程","x":1652,"y":-1740,"width":351,"height":60}, - {"id":"c8d6cc78d7f3a262","type":"text","text":"玩家已加入","x":1750,"y":-2009,"width":156,"height":60}, - {"id":"440f937f373d93ea","type":"text","text":"服务端 IO服务","x":1703,"y":-1340,"width":250,"height":60}, - {"id":"9e80714641f69c35","type":"text","text":"玩家线程","x":2280,"y":-1560,"width":155,"height":60} + {"id":"e39d3d8233e5dc17","type":"group","x":-1680,"y":-2600,"width":4135,"height":1080,"color":"#000000","label":"连接逻辑"}, + {"id":"d3236356b653fa83","type":"group","x":-1680,"y":-3200,"width":1952,"height":560,"color":"#000000","label":"项目整体结构"}, + {"id":"d2858e7dcde00ef4","type":"group","x":1136,"y":-1480,"width":1319,"height":694,"color":"#000000","label":"废弃:连接逻辑"}, + {"id":"b2ef8065341cd4b4","type":"group","x":-2720,"y":-1480,"width":975,"height":468,"color":"#000000","label":"游戏玩家"}, + {"id":"14281eab631d8297","type":"group","x":-2720,"y":-520,"width":975,"height":460,"color":"#000000","label":"游戏"}, + {"id":"c19d7f3e2e7c9dde","type":"text","text":"皮肤制作器","x":-421,"y":-3115,"width":250,"height":60,"color":"4"}, + {"id":"933e04c72421cba8","type":"text","text":"游戏实现框架","x":-1540,"y":-2951,"width":250,"height":60,"color":"5"}, + {"id":"2b9761ae59d3c1c1","type":"text","text":"服务端","x":-1180,"y":-2951,"width":250,"height":60,"color":"5"}, + {"id":"09f45096019f8e93","type":"text","text":"核心","x":-818,"y":-2951,"width":250,"height":60,"color":"1"}, + {"id":"4138f0aad76fc8c8","type":"text","text":"客户端","x":-480,"y":-2946,"width":250,"height":50,"color":"4"}, + {"id":"f16c9046ce342942","type":"text","text":"游戏","x":-1540,"y":-2760,"width":250,"height":60,"color":"5"}, + {"id":"f18abb5436784cee","type":"text","text":"软件操控端","x":-60,"y":-2951,"width":250,"height":60,"color":"4"}, + {"id":"991ab04aa54e93dc","type":"file","file":"doc/基础功能列表.md","x":-2720,"y":-3192,"width":975,"height":1672,"color":"#000000"}, + {"id":"ae51ac1a55eb30ff","type":"text","text":"**PLAYER - 玩家**","x":1156,"y":-1210,"width":250,"height":60}, + {"id":"0bbe75b82333d2f1","type":"text","text":"1. 自定义控制器界面\n2. 自定义控制器逻辑\n3. 自定义玩家信息","x":1406,"y":-900,"width":250,"height":94}, + {"id":"13029209c27cc6dc","type":"text","text":"**QR CODE - 二维码**","x":1566,"y":-1290,"width":250,"height":60}, + {"id":"970b836287034716","type":"text","text":"**CLIENT - 客户端**","x":1566,"y":-1130,"width":250,"height":50}, + {"id":"b25707346da6f026","type":"text","text":"服务端 入口服务","x":-422,"y":-1660,"width":250,"height":60,"color":"5"}, + {"id":"9e80714641f69c35","type":"text","text":"玩家线程","x":2280,"y":-1880,"width":155,"height":60,"color":"6"}, + {"id":"ee471e71ef3e4254","type":"text","text":"应用皮肤","x":732,"y":-2329,"width":250,"height":60,"color":"4"}, + {"id":"3d810b8ede0e7390","type":"text","text":"启用无皮肤模式","x":732,"y":-2160,"width":250,"height":60}, + {"id":"4ec22f6e59809493","type":"text","text":"重试","x":1232,"y":-2500,"width":250,"height":60,"color":"2"}, + {"id":"f4c69b65009040c9","type":"text","text":"向服务端发送加入信息","x":1232,"y":-2329,"width":250,"height":60,"color":"4"}, + {"id":"5d68be252ba5db2d","type":"text","text":"接收加入游戏信息","x":1232,"y":-2060,"width":250,"height":60}, + {"id":"e8680ff676132dd1","type":"text","text":"应用布局文件","x":-283,"y":-2329,"width":166,"height":60,"color":"4"}, + {"id":"a0b3d2f2fba623d9","type":"text","text":"失败","x":-268,"y":-2500,"width":96,"height":60,"color":"1"}, + {"id":"aa81015dae795f02","type":"text","text":"重试","x":192,"y":-2500,"width":240,"height":60,"color":"2"}, + {"id":"3d7d1730cb96f756","type":"text","text":"失败","x":-1193,"y":-2500,"width":242,"height":60,"color":"1"}, + {"id":"e9902af5372d0567","type":"text","text":"客户端","x":-1633,"y":-2329,"width":250,"height":60,"color":"4"}, + {"id":"61791938e8aa672c","type":"text","text":"从服务端下载布局文件","x":-648,"y":-2329,"width":240,"height":60,"color":"4"}, + {"id":"d149eb724bb75e1e","type":"text","text":"接收布局文件申请","x":-648,"y":-2060,"width":240,"height":60}, + {"id":"87d43cf10f79b197","type":"text","text":"接收连接申请","x":-1193,"y":-2060,"width":242,"height":60}, + {"id":"a7eb26e63ce25f28","type":"text","text":"从服务端下载皮肤文件","x":192,"y":-2329,"width":240,"height":60,"color":"4"}, + {"id":"8b783d24effb4218","type":"text","text":"接收皮肤文件申请","x":192,"y":-2060,"width":250,"height":60}, + {"id":"439f99fed6b0e32d","type":"text","text":"断开连接","x":-1508,"y":-2060,"width":164,"height":60,"color":"1"}, + {"id":"b4c5ae3611026103","type":"text","text":"连接到服务端","x":-1193,"y":-2329,"width":242,"height":60,"color":"4"}, + {"id":"7faa97ecc512481b","type":"text","text":"创建玩家实例,为该玩家启用专用线程","x":1652,"y":-2060,"width":351,"height":60}, + {"id":"440f937f373d93ea","type":"text","text":"服务端 IO服务","x":1703,"y":-1660,"width":250,"height":60,"color":"5"}, + {"id":"c8d6cc78d7f3a262","type":"text","text":"玩家已加入","x":1750,"y":-2329,"width":156,"height":60,"color":"6"}, + {"id":"274e9bdc3ed07a30","type":"text","text":"**ENGINE BINDING - 引擎绑定**","x":2151,"y":-1314,"width":284,"height":54}, + {"id":"693c49f59b413ba2","type":"text","text":"**CORE - 内核**","x":2168,"y":-1460,"width":250,"height":60}, + {"id":"6251c445b778d1a3","type":"text","text":"**支持的游戏**","x":2168,"y":-1200,"width":250,"height":50}, + {"id":"a3d79f917242ddd3","type":"text","text":"**不支持的游戏**","x":2168,"y":-1060,"width":250,"height":50}, + {"id":"49f56b9f31dff1b6","type":"file","file":"doc/Player - 玩家.md","x":-2165,"y":-1432,"width":400,"height":400,"color":"6"}, + {"id":"f3adb8605b9a6341","x":-3680,"y":-3192,"width":900,"height":1672,"color":"#000000","type":"link","url":"https://github.com/CatilGrass/ThereIsNoPads/projects"}, + {"id":"d9248e03fc8021e3","type":"file","file":"doc/Client - 客户端.md","x":-2700,"y":-1432,"width":400,"height":400,"color":"4"}, + {"id":"fc81e9051e8214a6","type":"file","file":"doc/Server - 服务器.md","x":-2700,"y":-480,"width":400,"height":400,"color":"5"}, + {"id":"28fa0236d42adda5","type":"text","text":"游戏","x":-2010,"y":-315,"width":245,"height":70,"color":"3"} ], "edges":[ {"id":"ce9b8719ff60e770","fromNode":"693c49f59b413ba2","fromSide":"bottom","toNode":"274e9bdc3ed07a30","toSide":"top","label":"导入"}, @@ -85,6 +96,13 @@ {"id":"7fa41a8b1d15da7a","fromNode":"7faa97ecc512481b","fromSide":"top","toNode":"c8d6cc78d7f3a262","toSide":"bottom","label":"回调到客户端"}, {"id":"d66a0a076b23e262","fromNode":"7faa97ecc512481b","fromSide":"right","toNode":"9e80714641f69c35","toSide":"top"}, {"id":"4bc2c01e0f472a4a","fromNode":"440f937f373d93ea","fromSide":"right","toNode":"9e80714641f69c35","toSide":"bottom"}, - {"id":"b0273cbfaea46103","fromNode":"9e80714641f69c35","fromSide":"bottom","toNode":"440f937f373d93ea","toSide":"right","label":"双工通信开始"} + {"id":"b0273cbfaea46103","fromNode":"9e80714641f69c35","fromSide":"bottom","toNode":"440f937f373d93ea","toSide":"right","label":"双工通信开始"}, + {"id":"c4bc830e9ca45069","fromNode":"d9248e03fc8021e3","fromSide":"bottom","toNode":"fc81e9051e8214a6","toSide":"top"}, + {"id":"ae7cad635a704300","fromNode":"fc81e9051e8214a6","fromSide":"top","toNode":"d9248e03fc8021e3","toSide":"bottom","label":"连接,传输皮肤与布局"}, + {"id":"0e42a676c8fb9953","fromNode":"fc81e9051e8214a6","fromSide":"right","toNode":"28fa0236d42adda5","toSide":"left"}, + {"id":"414fb80ca18d8584","fromNode":"28fa0236d42adda5","fromSide":"left","toNode":"fc81e9051e8214a6","toSide":"right","label":"-> 操作的输入\n<- 反馈的输出"}, + {"id":"73da2cced5e7485a","fromNode":"d9248e03fc8021e3","fromSide":"right","toNode":"49f56b9f31dff1b6","toSide":"left"}, + {"id":"5c3f6573b57538ae","fromNode":"49f56b9f31dff1b6","fromSide":"left","toNode":"d9248e03fc8021e3","toSide":"right"}, + {"id":"b5d846c12a376aea","fromNode":"49f56b9f31dff1b6","fromSide":"bottom","toNode":"fc81e9051e8214a6","toSide":"right","label":"发送:\n自身信息、操作信息"} ] }
\ No newline at end of file diff --git a/doc/Server - 服务器.md b/doc/Server - 服务器.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/Server - 服务器.md |
