diff options
| author | 1992414357@qq.com <1992414357@qq.com> | 2025-05-29 14:20:04 +0800 |
|---|---|---|
| committer | 1992414357@qq.com <1992414357@qq.com> | 2025-05-29 14:20:04 +0800 |
| commit | 3d23632d7c6330e0ec98dad53656dfed5f5767b0 (patch) | |
| tree | d8d59726ca5b046b55ba502f1b8d5ba3a561a569 /bindings/c | |
| parent | 9d3d6d4195e58c51166811d8357a331d53c4cdd4 (diff) | |
1. 优化了服务器的消息处理方式
2. 将c和c#的绑定移动到了./bindings/文件夹
Diffstat (limited to 'bindings/c')
| -rw-r--r-- | bindings/c/Cargo.toml | 15 | ||||
| -rw-r--r-- | bindings/c/build.rs | 47 | ||||
| -rw-r--r-- | bindings/c/src/data_c/common.rs | 6 | ||||
| -rw-r--r-- | bindings/c/src/data_c/connection_callback_message_c.rs | 129 | ||||
| -rw-r--r-- | bindings/c/src/data_c/connection_message_c.rs | 95 | ||||
| -rw-r--r-- | bindings/c/src/data_c/control_message_c.rs | 150 | ||||
| -rw-r--r-- | bindings/c/src/data_c/game_message_c.rs | 114 | ||||
| -rw-r--r-- | bindings/c/src/data_c/game_profile_c.rs | 101 | ||||
| -rw-r--r-- | bindings/c/src/data_c/mod.rs | 11 | ||||
| -rw-r--r-- | bindings/c/src/data_c/player_info_c.rs | 95 | ||||
| -rw-r--r-- | bindings/c/src/data_c/player_info_list_c.rs | 8 | ||||
| -rw-r--r-- | bindings/c/src/lib.rs | 41 | ||||
| -rw-r--r-- | bindings/c/src/service_c/common.rs | 4 | ||||
| -rw-r--r-- | bindings/c/src/service_c/mod.rs | 4 | ||||
| -rw-r--r-- | bindings/c/src/service_c/padclient.rs | 126 | ||||
| -rw-r--r-- | bindings/c/src/service_c/padserver.rs | 174 |
16 files changed, 1120 insertions, 0 deletions
diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml new file mode 100644 index 0000000..4f9f7f5 --- /dev/null +++ b/bindings/c/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nogamepads-c" + +workspace = "../../../NoGamepads" +edition = "2024" + +[dependencies] +nogamepads-core = { path = "../../core" } + +[build-dependencies] +cbindgen = "0.29.0" + +[lib] +name = "nogamepads_lib_c" +crate-type = [ "cdylib", "staticlib"]
\ No newline at end of file diff --git a/bindings/c/build.rs b/bindings/c/build.rs new file mode 100644 index 0000000..b5770a0 --- /dev/null +++ b/bindings/c/build.rs @@ -0,0 +1,47 @@ +use cbindgen::{generate_with_config, Config, ExportConfig, Language, ParseConfig, ParseExpandConfig}; +use std::env; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let config_c = Config { + language: Language::C, + header: Some("/* NoGamepads C Bindings. */".to_string()), + + parse: ParseConfig { + include: Some(vec!["*".to_string()]), + expand: ParseExpandConfig { + all_features: true, + ..Default::default() + }, + ..Default::default() + }, + + export: ExportConfig { + include: vec![ + "ConnectionMessageC".to_string(), + "ConnectionCallbackMessageC".to_string(), + "ControlMessageC".to_string(), + "GameMessageC".to_string(), + + "GameProfileC".to_string(), + "PlayerInfoC".to_string(), + ], + ..Default::default() + }, + ..Config::default() + }; + + let mut config_cpp = config_c.clone(); + config_cpp.language = Language::Cxx; + config_cpp.include_version = true; + config_cpp.header = Some("/* NoGamepads C++ Bindings. */".to_string()); + config_cpp.namespace = Some("nogamepads".to_string()); + + generate_with_config(&crate_dir, config_c) + .expect("Could not generate nogamepads.h") + .write_to_file("../../.cargo/shared/target/export/deps/nogamepads.h"); + generate_with_config(&crate_dir, config_cpp) + .expect("Could not generate nogamepads_cpp.h") + .write_to_file("../../.cargo/shared/target/export/deps/nogamepads_cpp.h"); +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/common.rs b/bindings/c/src/data_c/common.rs new file mode 100644 index 0000000..58c34c4 --- /dev/null +++ b/bindings/c/src/data_c/common.rs @@ -0,0 +1,6 @@ +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub struct KeyData { + pub(crate) key: u8 +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/connection_callback_message_c.rs b/bindings/c/src/data_c/connection_callback_message_c.rs new file mode 100644 index 0000000..9ded18b --- /dev/null +++ b/bindings/c/src/data_c/connection_callback_message_c.rs @@ -0,0 +1,129 @@ +use crate::data_c::game_profile_c::GameProfileC; +use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::{ConnectionCallbackMessage, ConnectionErrorType}; +use std::ffi::c_void; +use std::mem::ManuallyDrop; +use std::ptr::null_mut; + +#[repr(C)] +#[allow(unused_imports)] +pub struct ConnectionCallbackMessageC { + pub tag: ConnCallbackMsgCTag, + pub data: ConnCallbackMsgCUnion, +} + +#[repr(C)] +#[allow(unused_imports)] +pub enum ConnCallbackMsgCTag { + Profile, Deny, Fail, Ok, Welcome, Err +} + +#[repr(C)] +#[allow(unused_imports)] +pub union ConnCallbackMsgCUnion { + nul: *mut c_void, + profile: ManuallyDrop<GameProfileC>, + conn_err: ConnErrTypeC +} + +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub enum ConnErrTypeC { + ContainSamePlayer, PlayerBanned, Timeout, GameLocked, WhatTheHell +} + +impl From<ConnErrTypeC> for ConnectionErrorType { + fn from(value: ConnErrTypeC) -> Self { + match value { + ConnErrTypeC::ContainSamePlayer => { ConnectionErrorType::ContainSamePlayer } + ConnErrTypeC::PlayerBanned => { ConnectionErrorType::PlayerBanned } + ConnErrTypeC::Timeout => { ConnectionErrorType::Timeout } + ConnErrTypeC::GameLocked => { ConnectionErrorType::GameLocked } + ConnErrTypeC::WhatTheHell => { ConnectionErrorType::WhatTheHell } + } + } +} + +impl From<ConnectionErrorType> for ConnErrTypeC { + fn from(value: ConnectionErrorType) -> Self { + match value { + ConnectionErrorType::ContainSamePlayer => { ConnErrTypeC::ContainSamePlayer } + ConnectionErrorType::PlayerBanned => { ConnErrTypeC::PlayerBanned } + ConnectionErrorType::Timeout => { ConnErrTypeC::Timeout } + ConnectionErrorType::GameLocked => { ConnErrTypeC::GameLocked } + ConnectionErrorType::WhatTheHell => { ConnErrTypeC::WhatTheHell } + } + } +} + +impl From<ConnectionCallbackMessageC> for ConnectionCallbackMessage { + fn from(mut value: ConnectionCallbackMessageC) -> Self { + match value.tag { + ConnCallbackMsgCTag::Profile => unsafe { + let profile = ManuallyDrop::take(&mut value.data.profile); + ConnectionCallbackMessage::Profile(profile.into()) + } + ConnCallbackMsgCTag::Deny => unsafe { + ConnectionCallbackMessage::Deny(value.data.conn_err.into()) + } + ConnCallbackMsgCTag::Fail => unsafe { + ConnectionCallbackMessage::Fail(value.data.conn_err.into()) + } + ConnCallbackMsgCTag::Ok => { + ConnectionCallbackMessage::Ok + + } + ConnCallbackMsgCTag::Welcome => { + ConnectionCallbackMessage::Welcome + } + ConnCallbackMsgCTag::Err => { + ConnectionCallbackMessage::Err + } + } + } +} + +impl From<ConnectionCallbackMessage> for ConnectionCallbackMessageC { + fn from(value: ConnectionCallbackMessage) -> Self { + match value { + ConnectionCallbackMessage::Profile(profile) => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Profile, + data: ConnCallbackMsgCUnion { + profile: ManuallyDrop::new(profile.into()) + } + } + } + ConnectionCallbackMessage::Deny(conn_err) => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Deny, + data: ConnCallbackMsgCUnion { conn_err: conn_err.into() } + } + } + ConnectionCallbackMessage::Fail(conn_err) => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Fail, + data: ConnCallbackMsgCUnion { conn_err: conn_err.into() } + } + } + ConnectionCallbackMessage::Ok => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Ok, + data: ConnCallbackMsgCUnion { nul: null_mut() } + } + } + ConnectionCallbackMessage::Welcome => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Welcome, + data: ConnCallbackMsgCUnion { nul: null_mut() } + } + } + ConnectionCallbackMessage::Err => { + ConnectionCallbackMessageC { + tag: ConnCallbackMsgCTag::Err, + data: ConnCallbackMsgCUnion { nul: null_mut() } + } + } + } + } +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/connection_message_c.rs b/bindings/c/src/data_c/connection_message_c.rs new file mode 100644 index 0000000..b3b54d4 --- /dev/null +++ b/bindings/c/src/data_c/connection_message_c.rs @@ -0,0 +1,95 @@ +use crate::data_c::connection_message_c::ConnMsgCTag::{Connection, Ready, RequestLayoutConfigure, RequestProfile, RequestSkinPackage}; +use std::ffi::c_void; +use std::mem::ManuallyDrop; +use std::ptr::null_mut; +use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::ConnectionMessage; +use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; + +#[repr(C)] +#[allow(unused_imports)] +pub struct ConnectionMessageC { + pub tag: ConnMsgCTag, + pub data: ConnMsgCUnion +} + +#[repr(C)] +#[allow(unused_imports)] +pub enum ConnMsgCTag { + Connection, RequestProfile, RequestLayoutConfigure, RequestSkinPackage, Ready, Err +} + +#[repr(C)] +#[allow(unused_imports)] +pub union ConnMsgCUnion { + nul: *mut c_void, + info: ManuallyDrop<PlayerInfo> +} + +impl From<ConnectionMessageC> for ConnectionMessage { + fn from(mut value: ConnectionMessageC) -> Self { + match value.tag { + Connection => unsafe { + let info = ManuallyDrop::take(&mut value.data.info); + ConnectionMessage::Connection(info) + } + RequestProfile => { + ConnectionMessage::RequestProfile + } + RequestLayoutConfigure => { + ConnectionMessage::RequestLayoutConfigure + } + RequestSkinPackage => { + ConnectionMessage::RequestSkinPackage + } + Ready => { + ConnectionMessage::Ready + } + ConnMsgCTag::Err => { + ConnectionMessage::Err + } + } + } +} + +impl From<ConnectionMessage> for ConnectionMessageC { + fn from(value: ConnectionMessage) -> Self { + match value { + ConnectionMessage::Connection(info) => { + ConnectionMessageC { + tag: Connection, + data: ConnMsgCUnion { info: ManuallyDrop::new(info) } + } + } + ConnectionMessage::RequestProfile => { + ConnectionMessageC { + tag: RequestProfile, + data: ConnMsgCUnion { nul: null_mut() } + } + } + ConnectionMessage::RequestLayoutConfigure => { + ConnectionMessageC { + tag: RequestLayoutConfigure, + data: ConnMsgCUnion { nul: null_mut() } + } + } + ConnectionMessage::RequestSkinPackage => { + ConnectionMessageC { + tag: RequestSkinPackage, + data: ConnMsgCUnion { nul: null_mut() } + } + } + ConnectionMessage::Ready => { + ConnectionMessageC { + tag: Ready, + data: ConnMsgCUnion { nul: null_mut() } + } + } + ConnectionMessage::Err => { + ConnectionMessageC { + tag: ConnMsgCTag::Err, + data: ConnMsgCUnion { nul: null_mut() } + } + } + } + } +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/control_message_c.rs b/bindings/c/src/data_c/control_message_c.rs new file mode 100644 index 0000000..6868459 --- /dev/null +++ b/bindings/c/src/data_c/control_message_c.rs @@ -0,0 +1,150 @@ +use crate::data_c::common::KeyData; +use crate::{str_c_to_rs, str_rs_to_c}; +use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::ControlMessage; +use std::ffi::{c_char, c_void}; +use std::ptr::null_mut; + +#[repr(C)] +#[allow(unused_imports)] +pub struct ControlMessageC { + pub tag: CtrlMsgCTag, + pub data: CtrlMsgCUnion +} + +#[repr(C)] +#[allow(unused_imports)] +pub enum CtrlMsgCTag { + Msg, Pressed, Released, Axis, Dir, Exit, Err +} + +#[repr(C)] +#[allow(unused_imports)] +pub union CtrlMsgCUnion { + nul: *mut c_void, + key: KeyData, + axis: AxisData, + dir: DirData, + str: StrData +} + +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub struct AxisData { + key: u8, + ax: f64 +} + +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub struct DirData { + key: u8, + x: f64, + y: f64 +} + +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub struct StrData { + str: *const c_char +} + +impl From<ControlMessageC> for ControlMessage { + fn from(msg_c: ControlMessageC) -> Self { + match msg_c.tag { + CtrlMsgCTag::Msg => unsafe { + let str = str_c_to_rs(msg_c.data.str.str); + if str.is_some() { + ControlMessage::Msg(str.unwrap()) + } else { + ControlMessage::Err + } + } + CtrlMsgCTag::Pressed => unsafe { + ControlMessage::Pressed(msg_c.data.key.key) + } + CtrlMsgCTag::Released => unsafe { + ControlMessage::Released(msg_c.data.key.key) + } + CtrlMsgCTag::Axis => unsafe { + let data = msg_c.data.axis; + ControlMessage::Axis(data.key, data.ax) + } + CtrlMsgCTag::Dir => unsafe { + let data = msg_c.data.dir; + ControlMessage::Dir(data.key, (data.x, data.y)) + } + CtrlMsgCTag::Exit => { + ControlMessage::Exit + } + CtrlMsgCTag::Err => { + ControlMessage::Err + } + } + } +} + +impl From<ControlMessage> for ControlMessageC { + fn from(msg_rs: ControlMessage) -> Self { + match msg_rs { + ControlMessage::Msg(str_msg) => { + ControlMessageC { + tag: CtrlMsgCTag::Msg, + data: CtrlMsgCUnion { + str: StrData { str: str_rs_to_c(str_msg).1 }, + } + } + } + ControlMessage::Pressed(key) => { + ControlMessageC { + tag: CtrlMsgCTag::Pressed, + data: CtrlMsgCUnion { + key: KeyData { key }, + } + } + } + ControlMessage::Released(key) => { + ControlMessageC { + tag: CtrlMsgCTag::Released, + data: CtrlMsgCUnion { + key: KeyData { key }, + } + } + } + ControlMessage::Axis(key, ax) => { + ControlMessageC { + tag: CtrlMsgCTag::Axis, + data: CtrlMsgCUnion { + axis: AxisData { key, ax } + } + } + } + ControlMessage::Dir(key, (x, y)) => { + ControlMessageC { + tag: CtrlMsgCTag::Dir, + data: CtrlMsgCUnion { + dir: DirData { key, x, y } + } + } + } + ControlMessage::Exit => { + ControlMessageC { + tag: CtrlMsgCTag::Exit, + data: CtrlMsgCUnion { + nul: null_mut(), + } + } + } + ControlMessage::Err => { + ControlMessageC { + tag: CtrlMsgCTag::Err, + data: CtrlMsgCUnion { + nul: null_mut(), + } + } + } + } + } +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/game_message_c.rs b/bindings/c/src/data_c/game_message_c.rs new file mode 100644 index 0000000..946ce60 --- /dev/null +++ b/bindings/c/src/data_c/game_message_c.rs @@ -0,0 +1,114 @@ +use std::ffi::c_void; +use std::ptr::null_mut; +use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::{GameMessage, LeaveReason}; +use crate::data_c::common::KeyData; + +#[repr(C)] +#[allow(unused_imports)] +pub struct GameMessageC { + pub tag: GameMsgCTag, + pub data: GameMsgCUnion +} + + +#[repr(C)] +#[allow(unused_imports)] +pub enum GameMsgCTag { + SkinEventTrigger, DisableKey, EnableKey, Leave, Err +} + +#[repr(C)] +#[allow(unused_imports)] +pub union GameMsgCUnion { + nul: *mut c_void, + key: KeyData, + reason: LeaveReasonData +} + +#[repr(C)] +#[allow(unused_imports)] +#[derive(Copy, Clone)] +pub enum LeaveReasonData { + GameOver, ServerClosed, YouAreKicked, YouAreBanned +} + +impl From<LeaveReasonData> for LeaveReason { + fn from(value: LeaveReasonData) -> Self { + match value { + LeaveReasonData::GameOver => { LeaveReason::GameOver } + LeaveReasonData::ServerClosed => { LeaveReason::ServerClosed } + LeaveReasonData::YouAreKicked => { LeaveReason::YouAreKicked } + LeaveReasonData::YouAreBanned => { LeaveReason::YouAreBanned } + } + } +} + +impl From<LeaveReason> for LeaveReasonData { + fn from(value: LeaveReason) -> Self { + match value { + LeaveReason::GameOver => { LeaveReasonData::GameOver } + LeaveReason::ServerClosed => { LeaveReasonData::ServerClosed } + LeaveReason::YouAreKicked => { LeaveReasonData::YouAreKicked } + LeaveReason::YouAreBanned => { LeaveReasonData::YouAreBanned } + } + } +} + +impl From<GameMessageC> for GameMessage { + fn from(msg: GameMessageC) -> Self { + match msg.tag { + GameMsgCTag::SkinEventTrigger => unsafe { + GameMessage::SkinEventTrigger(msg.data.key.key) + } + GameMsgCTag::DisableKey => unsafe { + GameMessage::DisableKey(msg.data.key.key) + } + GameMsgCTag::EnableKey => unsafe { + GameMessage::EnableKey(msg.data.key.key) + } + GameMsgCTag::Leave => unsafe { + GameMessage::Leave(msg.data.reason.into()) + } + GameMsgCTag::Err => { + GameMessage::Err + } + } + } +} + +impl From<GameMessage> for GameMessageC { + fn from(msg: GameMessage) -> Self { + match msg { + GameMessage::SkinEventTrigger(key) => { + GameMessageC { + tag: GameMsgCTag::SkinEventTrigger, + data: GameMsgCUnion { key: KeyData { key } } + } + } + GameMessage::DisableKey(key) => { + GameMessageC { + tag: GameMsgCTag::DisableKey, + data: GameMsgCUnion { key: KeyData { key } } + } + } + GameMessage::EnableKey(key) => { + GameMessageC { + tag: GameMsgCTag::EnableKey, + data: GameMsgCUnion { key: KeyData { key } } + } + } + GameMessage::Leave(reason) => { + GameMessageC { + tag: GameMsgCTag::Leave, + data: GameMsgCUnion { reason: reason.into() } + } + } + GameMessage::Err => { + GameMessageC { + tag: GameMsgCTag::Err, + data: GameMsgCUnion { nul: null_mut() } + } + } + } + } +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/game_profile_c.rs b/bindings/c/src/data_c/game_profile_c.rs new file mode 100644 index 0000000..1dd11f7 --- /dev/null +++ b/bindings/c/src/data_c/game_profile_c.rs @@ -0,0 +1,101 @@ +use crate::{str_c_to_rs, str_rs_to_c}; +use nogamepads_lib_rs::pad_data::game_profile::game_profile::GameProfile; +use std::ffi::c_char; + +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(unused_imports)] +pub struct GameProfileC { + pub game_name: *const c_char, + pub game_description: *const c_char, + pub organization: *const c_char, + pub version: *const c_char, + pub website: *const c_char, + pub email: *const c_char +} + +impl From<GameProfileC> for GameProfile { + fn from(value: GameProfileC) -> Self { + let unknown = "".to_string(); + GameProfile { + game_name: str_c_to_rs(value.game_name).unwrap_or(unknown.clone()), + game_description: str_c_to_rs(value.game_description).unwrap_or(unknown.clone()), + organization: str_c_to_rs(value.organization).unwrap_or(unknown.clone()), + version: str_c_to_rs(value.version).unwrap_or(unknown.clone()), + website: str_c_to_rs(value.website).unwrap_or(unknown.clone()), + email: str_c_to_rs(value.email).unwrap_or(unknown.clone()) + } + } +} + +impl From<GameProfile> for GameProfileC { + fn from(value: GameProfile) -> Self { + GameProfileC { + game_name: str_rs_to_c(value.game_name).1, + game_description: str_rs_to_c(value.game_description).1, + organization: str_rs_to_c(value.organization).1, + version: str_rs_to_c(value.version).1, + website: str_rs_to_c(value.website).1, + email: str_rs_to_c(value.email).1 + } + } +} + +impl From<&GameProfileC> for &GameProfile { + fn from(value: &GameProfileC) -> Self { + value.into() + } +} + +impl From<&GameProfile> for &GameProfileC { + fn from(value: &GameProfile) -> Self { + value.into() + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn init_game_profile() -> GameProfileC { + GameProfile::default().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_name(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.game_name(value.as_str()).to_owned().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_description(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.game_description(value.as_str()).to_owned().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_organization(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.organization(value.as_str()).to_owned().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_version(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.version(value.as_str()).to_owned().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_website(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.website(value.as_str()).to_owned().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_game_profile_email(game_profile: GameProfileC, value: *const c_char) -> GameProfileC { + let value = str_c_to_rs(value).unwrap_or_default(); + let mut raw = GameProfile::from(game_profile); + raw.email(value.as_str()).to_owned().into() +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/mod.rs b/bindings/c/src/data_c/mod.rs new file mode 100644 index 0000000..afe8728 --- /dev/null +++ b/bindings/c/src/data_c/mod.rs @@ -0,0 +1,11 @@ +pub mod common; + +pub mod control_message_c; +pub mod game_message_c; +pub mod connection_message_c; +pub mod connection_callback_message_c; + + +pub mod game_profile_c; +pub mod player_info_c; +pub mod player_info_list_c;
\ No newline at end of file diff --git a/bindings/c/src/data_c/player_info_c.rs b/bindings/c/src/data_c/player_info_c.rs new file mode 100644 index 0000000..b8a54e6 --- /dev/null +++ b/bindings/c/src/data_c/player_info_c.rs @@ -0,0 +1,95 @@ +use crate::{str_c_to_rs, str_rs_to_c}; +use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::{PlayerAccountInfo, PlayerCustomizeInfo, PlayerInfo}; +use std::ffi::c_char; + +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(unused_imports)] +pub struct PlayerInfoC { + pub account_id: *const c_char, + pub account_hash: *const c_char, + + pub customize_nickname: *const c_char, + pub customize_color_hue: i32, + pub customize_color_saturation: f64, + pub customize_color_value: f64, +} + +impl From<PlayerInfoC> for PlayerInfo { + fn from(value: PlayerInfoC) -> Self { + let unknown = "unknown".to_string(); + PlayerInfo { + account : PlayerAccountInfo { + id: str_c_to_rs(value.account_id).unwrap_or(unknown.clone()), + player_hash: str_c_to_rs(value.account_hash).unwrap_or(unknown.clone()), + }, + customize: PlayerCustomizeInfo { + nickname: str_c_to_rs(value.customize_nickname).unwrap_or(unknown.clone()), + color_hue: value.customize_color_hue, + color_saturation: value.customize_color_saturation, + color_value: value.customize_color_value, + } + } + } +} + +impl From<PlayerInfo> for PlayerInfoC { + fn from(value: PlayerInfo) -> Self { + PlayerInfoC { + account_id: str_rs_to_c(value.account.id).1, + account_hash: str_rs_to_c(value.account.player_hash).1, + customize_nickname: str_rs_to_c(value.customize.nickname).1, + customize_color_hue: value.customize.color_hue, + customize_color_saturation: value.customize.color_saturation, + customize_color_value: value.customize.color_value, + } + } +} + +impl From<&PlayerInfoC> for &PlayerInfo { + fn from(value: &PlayerInfoC) -> Self { + value.into() + } +} + +impl From<&PlayerInfo> for &PlayerInfoC { + fn from(value: &PlayerInfo) -> Self { + value.into() + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn init_player_info() -> PlayerInfoC { + PlayerInfo::new().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_player_info_account( + info: PlayerInfoC, id: *const c_char, password: *const c_char) -> PlayerInfoC { + + let id = str_c_to_rs(id).unwrap_or("".to_string()); + let password = str_c_to_rs(password).unwrap_or("".to_string()); + + let mut raw_info = PlayerInfo::from(info); + raw_info.setup_account_info(id.as_str(), password.as_str()); + raw_info.into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_player_info_color( + info: PlayerInfoC, h: i32, s: f64, v: f64) -> PlayerInfoC { + + let mut raw_info = PlayerInfo::from(info); + raw_info.set_customize_color_hsv(h, s, v); + raw_info.into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_player_info_nickname( + info: PlayerInfoC, nickname: *const c_char) -> PlayerInfoC { + let nickname = str_c_to_rs(nickname).unwrap_or("".to_string()); + + let mut raw_info = PlayerInfo::from(info); + raw_info.set_nickname(nickname.as_str()); + raw_info.into() +}
\ No newline at end of file diff --git a/bindings/c/src/data_c/player_info_list_c.rs b/bindings/c/src/data_c/player_info_list_c.rs new file mode 100644 index 0000000..99d5c35 --- /dev/null +++ b/bindings/c/src/data_c/player_info_list_c.rs @@ -0,0 +1,8 @@ +use crate::data_c::player_info_c::PlayerInfoC; + +#[repr(C)] +pub struct PlayerList { + pub(crate) players: *mut PlayerInfoC, + pub(crate) len: usize, + pub(crate) capacity: usize, +}
\ No newline at end of file diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs new file mode 100644 index 0000000..6efd0e9 --- /dev/null +++ b/bindings/c/src/lib.rs @@ -0,0 +1,41 @@ +use std::ffi::{c_char, CStr, CString}; + +pub mod data_c; +pub mod service_c; + +pub fn str_rs_to_c(s: String) -> (CString, *const c_char) { + let c_string = CString::new(s).expect(""); + let ptr = c_string.as_ptr(); + (c_string, ptr) +} + +pub fn str_c_to_rs(ptr: *const c_char) -> Option<String> { + if ptr.is_null() { + return None; + } + unsafe { + CStr::from_ptr(ptr) + .to_str() + .ok() + .map(|s| s.to_owned()) + } +} + +#[macro_export] +macro_rules! box_into_raw { + ($expr:expr) => { + $crate::box_into_raw!(@inner $expr) + }; + + ($expr:expr => $t:ty) => { + $crate::box_into_raw!(@inner ($expr) as $t) + }; + + (@inner $expr:expr) => { + ::std::boxed::Box::into_raw(::std::boxed::Box::new($expr)) + }; + + (@inner ($expr:expr) as $t:ty) => { + ::std::boxed::Box::into_raw(::std::boxed::Box::new($expr) as ::std::boxed::Box<$t>) + }; +}
\ No newline at end of file diff --git a/bindings/c/src/service_c/common.rs b/bindings/c/src/service_c/common.rs new file mode 100644 index 0000000..8b13da0 --- /dev/null +++ b/bindings/c/src/service_c/common.rs @@ -0,0 +1,4 @@ +use nogamepads_lib_rs::DEFAULT_PORT; + +#[unsafe(no_mangle)] +pub extern "C" fn get_default_port() -> u16 { DEFAULT_PORT }
\ No newline at end of file diff --git a/bindings/c/src/service_c/mod.rs b/bindings/c/src/service_c/mod.rs new file mode 100644 index 0000000..674baf7 --- /dev/null +++ b/bindings/c/src/service_c/mod.rs @@ -0,0 +1,4 @@ +pub mod common; + +pub mod padclient; +pub mod padserver;
\ No newline at end of file diff --git a/bindings/c/src/service_c/padclient.rs b/bindings/c/src/service_c/padclient.rs new file mode 100644 index 0000000..b7bac43 --- /dev/null +++ b/bindings/c/src/service_c/padclient.rs @@ -0,0 +1,126 @@ +use crate::data_c::control_message_c::ControlMessageC; +use crate::data_c::game_message_c::GameMessageC; +use crate::data_c::player_info_c::PlayerInfoC; +use crate::{box_into_raw, str_c_to_rs, str_rs_to_c}; +use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::GameMessage; +use nogamepads_lib_rs::pad_service::client::nogamepads_client::PadClient; +use nogamepads_lib_rs::DEFAULT_PORT; +use std::ffi::c_char; +use std::net::{IpAddr, Ipv4Addr}; +use std::str::FromStr; + +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(unused_imports)] +pub struct PadClientC { + pub target_address: *const c_char, + pub target_port: u16, + pub bind_player: PlayerInfoC, + pub enable_console: bool, + pub quiet: bool +} + +impl From<PadClientC> for PadClient { + fn from(value: PadClientC) -> Self { + let address = IpAddr::from_str(str_c_to_rs(value.target_address).unwrap_or("".to_string()).as_str()).unwrap_or( + IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)) + ); + let port = value.target_port; + let mut client = PadClient::bind_addr_with_port(address, port); + client.bind_player(value.bind_player.into()); + if value.enable_console { client.enable_console(); } + if value.quiet { client.quiet(); } + client + } +} + +impl From<PadClient> for PadClientC { + fn from(mut value: PadClient) -> Self { + let (addr, port) = value.clone_addr(); + + PadClientC { + target_address: str_rs_to_c(addr.to_string()).1, + target_port: port, + bind_player: value.unbind_player().into(), + enable_console: value.is_enable_console(), + quiet: value.is_quiet(), + } + } +} +impl From<&mut PadClient> for PadClientC { + fn from(value: &mut PadClient) -> Self { + value.into() + } +} + +// 构建 + +#[unsafe(no_mangle)] +pub extern "C" fn init_client(address: *const c_char) -> PadClientC { + let addr_str = str_c_to_rs(address).unwrap_or("127.0.0.1".to_string()); + let addr_v4 = Ipv4Addr::from_str(addr_str.as_str()).unwrap_or( + Ipv4Addr::new(127, 0, 0, 1)); + PadClient::bind_addr_with_port(IpAddr::from(addr_v4), DEFAULT_PORT).into() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn init_client_with_port(address: *const c_char, port: u16) -> PadClientC { + let addr_str = str_c_to_rs(address).unwrap_or("127.0.0.1".to_string()); + let addr_v4 = Ipv4Addr::from_str(addr_str.as_str()).unwrap_or( + Ipv4Addr::new(127, 0, 0, 1)); + PadClient::bind_addr_with_port(IpAddr::from(addr_v4), port).into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_client_quiet(client: PadClientC) -> PadClientC { + let mut raw: PadClient = client.into(); + raw.quiet().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn enable_client_console(client: PadClientC) -> PadClientC { + let mut raw: PadClient = client.into(); + raw.enable_console().into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn bind_player_to_client(client: PadClientC, info: PlayerInfoC) -> PadClientC { + let mut raw: PadClient = client.into(); + raw.bind_player(info.into()).into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn complete(client: PadClientC) -> *mut PadClient { + let client: PadClient = client.into(); + box_into_raw!(client) +} + +// 状态控制 + +#[unsafe(no_mangle)] +pub extern "C" fn connect_client_to_server(client: &mut PadClient) { + let client = unsafe { Box::from_raw(client) }; + client.connect(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn exit_client_from_server(client: &PadClient) { + client.exit_server(); +} + +// 消息管理 + +#[unsafe(no_mangle)] +pub extern "C" fn put_a_msg_to_server(client: &PadClient, msg: ControlMessageC) { + client.put_msg(msg.into()) +} + +#[unsafe(no_mangle)] +pub extern "C" fn pop_a_msg_from_server(client: &PadClient) -> GameMessageC { + client.pop_a_msg().unwrap_or(GameMessage::Err).into() +} + +#[unsafe(no_mangle)] +pub extern "C" fn pop_msg_from_server_or(client: &PadClient, or: GameMessageC) -> GameMessageC { + client.pop_msg_or(or.into()).into() +}
\ No newline at end of file diff --git a/bindings/c/src/service_c/padserver.rs b/bindings/c/src/service_c/padserver.rs new file mode 100644 index 0000000..98c5792 --- /dev/null +++ b/bindings/c/src/service_c/padserver.rs @@ -0,0 +1,174 @@ +use crate::data_c::game_message_c::GameMessageC; +use crate::data_c::game_profile_c::GameProfileC; +use crate::data_c::player_info_c::PlayerInfoC; +use crate::data_c::player_info_list_c::PlayerList; +use crate::{box_into_raw, str_c_to_rs}; +use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; +use nogamepads_lib_rs::pad_service::server::nogamepads_server::PadServer; +use nogamepads_lib_rs::DEFAULT_PORT; +use std::collections::HashMap; +use std::ffi::c_char; +use std::net::{IpAddr, Ipv4Addr}; +use std::str::FromStr; +use std::sync::{Arc, MutexGuard, PoisonError}; + +// 构建 + +#[unsafe(no_mangle)] +pub extern "C" fn init_server(address: *const c_char) -> *mut PadServer { + let addr_str = str_c_to_rs(address).unwrap_or("127.0.0.1".to_string()); + let addr_v4 = Ipv4Addr::from_str(addr_str.as_str()).unwrap_or( + Ipv4Addr::new(127, 0, 0, 1)); + let mut server = PadServer::default(); + server.addr(IpAddr::from(addr_v4), DEFAULT_PORT); + box_into_raw!(server) +} + +#[unsafe(no_mangle)] +pub extern "C" fn init_server_with_port(address: *const c_char, port: u16) -> *mut PadServer { + let addr_str = str_c_to_rs(address).unwrap_or("127.0.0.1".to_string()); + let addr_v4 = Ipv4Addr::from_str(addr_str.as_str()).unwrap_or( + Ipv4Addr::new(127, 0, 0, 1)); + let mut server = PadServer::default(); + server.addr(IpAddr::from(addr_v4), port); + box_into_raw!(server) +} + +#[unsafe(no_mangle)] +pub extern "C" fn set_server_quiet(server: &mut PadServer) { + server.quiet(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn enable_server_console(server: &mut PadServer) { + server.enable_console(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn bind_profile_to_server(server: &mut PadServer, profile: &GameProfileC) { + server.put_profile(profile.clone().into()); +} + +// 状态控制 + +#[unsafe(no_mangle)] +pub extern "C" fn start_server(server: *mut PadServer) { + let server = unsafe { Box::from_raw(server) }; + let arc = Arc::new(server.as_ref().clone()); + PadServer::start_server(arc); +} + +#[unsafe(no_mangle)] +pub extern "C" fn stop_server(server: *mut PadServer) { + let server = unsafe { Box::from_raw(server) }; + server.stop_server(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn lock_game_on_server(server: *mut PadServer) { + let server = unsafe { Box::from_raw(server) }; + server.lock_game(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn unlock_game_on_server(server: *mut PadServer) { + let server = unsafe { Box::from_raw(server) }; + server.unlock_game(); +} + +#[unsafe(no_mangle)] +pub extern "C" fn is_server_game_locked(server: *mut PadServer) -> bool { + let server = unsafe { Box::from_raw(server) }; + server.is_game_locked() +} + +// 消息管理 +#[unsafe(no_mangle)] +pub extern "C" fn put_a_msg_to_player(server: *mut PadServer, msg: GameMessageC, player: &PlayerInfoC) { + let server = unsafe { Box::from_raw(server) }; + server.put_msg_to(msg.into(), player.into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn put_msg_to_all_players(server: *mut PadServer, msg: GameMessageC) { + let server = unsafe { Box::from_raw(server) }; + server.put_msg_to_all(&msg.into()); +} + +// #[unsafe(no_mangle)] +// pub extern "C" fn pop_a_msg(server: *mut PadServer) -> ControlMessageC { +// let server = unsafe { Box::from_raw(server) }; +// let msg = server.pop_a_msg(); +// if msg.is_some() { +// let (player, msg) = msg.unwrap(); +// } +// } + +// 玩家管理 +#[unsafe(no_mangle)] +pub extern "C" fn is_player_online(server: *mut PadServer, player: &PlayerInfoC) -> bool { + let server = unsafe { Box::from_raw(server) }; + server.is_player_online(player.into()) +} + +#[unsafe(no_mangle)] +pub extern "C" fn is_player_banned(server: *mut PadServer, player: &PlayerInfoC) -> bool { + let server = unsafe { Box::from_raw(server) }; + server.is_player_banned(player.into()) +} + +#[unsafe(no_mangle)] +pub extern "C" fn kick_player(server: *mut PadServer, player: &PlayerInfoC) { + let server = unsafe { Box::from_raw(server) }; + server.kick_player(player.into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn ban_player(server: *mut PadServer, player: &PlayerInfoC) { + let server = unsafe { Box::from_raw(server) }; + server.ban_player(player.into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn pardon_player(server: *mut PadServer, player: &PlayerInfoC) { + let server = unsafe { Box::from_raw(server) }; + server.pardon_player(player.into()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn list_online_players(server: *mut PadServer) -> PlayerList { + let server = unsafe { Box::from_raw(server) }; + let list = server.list_players(); + process_list_result(list) +} + +#[unsafe(no_mangle)] +pub extern "C" fn list_banned_players(server: *mut PadServer) -> PlayerList { + let server = unsafe { Box::from_raw(server) }; + let list = server.list_players_banned(); + process_list_result(list) +} + +fn process_list_result(list: Result<Vec<PlayerInfo>, PoisonError<MutexGuard<HashMap<String, PlayerInfo>>>>) -> PlayerList { + let result : Vec<PlayerInfo>; + if list.is_ok() { + result = list.unwrap(); + } else { + result = vec![]; + } + let mut result_c: Vec<PlayerInfoC> = Vec::new(); + for item in result { + result_c.push(item.into()); + } + + let mut vec = result_c.into_boxed_slice(); + let ptr = vec.as_mut_ptr(); + let len = vec.len(); + let capacity = vec.len(); + + PlayerList { + players: ptr, + len, + capacity + } +}
\ No newline at end of file |
