aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author1992414357@qq.com <1992414357@qq.com>2025-05-28 09:08:16 +0800
committer1992414357@qq.com <1992414357@qq.com>2025-05-28 09:08:16 +0800
commit11b7b6f05bcf682d1616a74029a4415a05de9fc0 (patch)
tree0ab7b9fea645dadf75ef56840e259d56f5e54beb
parentc470dff02c2c4def065efa59dd04026ca50dc090 (diff)
增加 C/C++ 的绑定
-rw-r--r--core_c/build.rs33
-rw-r--r--core_c/src/data_c/common.rs6
-rw-r--r--core_c/src/data_c/connection_callback_message_c.rs129
-rw-r--r--core_c/src/data_c/connection_message_c.rs95
-rw-r--r--core_c/src/data_c/control_message_c.rs150
-rw-r--r--core_c/src/data_c/game_message_c.rs114
-rw-r--r--core_c/src/data_c/game_profile_c.rs44
-rw-r--r--core_c/src/data_c/mod.rs11
-rw-r--r--core_c/src/data_c/player_info_c.rs33
-rw-r--r--core_c/src/data_c/player_info_list_c.rs8
-rw-r--r--core_c/src/service_c/common.rs4
-rw-r--r--core_c/src/service_c/mod.rs4
-rw-r--r--core_c/src/service_c/padclient.rs73
-rw-r--r--core_c/src/service_c/padserver.rs175
14 files changed, 879 insertions, 0 deletions
diff --git a/core_c/build.rs b/core_c/build.rs
new file mode 100644
index 0000000..1dd893b
--- /dev/null
+++ b/core_c/build.rs
@@ -0,0 +1,33 @@
+use cbindgen::{generate_with_config, Config, 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 {
+ expand: ParseExpandConfig {
+ all_features: true,
+ ..Default::default()
+ },
+ ..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/release/deps/nogamepads.h");
+ generate_with_config(&crate_dir, config_cpp)
+ .expect("Could not generate nogamepads_cpp.h")
+ .write_to_file("../.cargo/shared/target/release/deps/nogamepads_cpp.h");
+} \ No newline at end of file
diff --git a/core_c/src/data_c/common.rs b/core_c/src/data_c/common.rs
new file mode 100644
index 0000000..58c34c4
--- /dev/null
+++ b/core_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/core_c/src/data_c/connection_callback_message_c.rs b/core_c/src/data_c/connection_callback_message_c.rs
new file mode 100644
index 0000000..bb96a0d
--- /dev/null
+++ b/core_c/src/data_c/connection_callback_message_c.rs
@@ -0,0 +1,129 @@
+use std::ffi::c_void;
+use std::mem::ManuallyDrop;
+use std::ptr::null_mut;
+use nogamepads_lib_rs::pad_data::game_profile::game_profile::GameProfile;
+use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::{ConnectionCallbackMessage, ConnectionErrorType};
+
+#[repr(C)]
+#[allow(unused_imports)]
+pub struct ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag,
+ 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<GameProfile>,
+ 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<ConnCallbackMsgC> for ConnectionCallbackMessage {
+ fn from(mut value: ConnCallbackMsgC) -> Self {
+ match value.tag {
+ ConnCallbackMsgCTag::Profile => unsafe {
+ let profile = ManuallyDrop::take(&mut value.data.profile);
+ ConnectionCallbackMessage::Profile(profile)
+ }
+ 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 ConnCallbackMsgC {
+ fn from(value: ConnectionCallbackMessage) -> Self {
+ match value {
+ ConnectionCallbackMessage::Profile(profile) => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Profile,
+ data: ConnCallbackMsgCUnion {
+ profile: ManuallyDrop::new(profile)
+ }
+ }
+ }
+ ConnectionCallbackMessage::Deny(conn_err) => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Deny,
+ data: ConnCallbackMsgCUnion { conn_err: conn_err.into() }
+ }
+ }
+ ConnectionCallbackMessage::Fail(conn_err) => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Fail,
+ data: ConnCallbackMsgCUnion { conn_err: conn_err.into() }
+ }
+ }
+ ConnectionCallbackMessage::Ok => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Ok,
+ data: ConnCallbackMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionCallbackMessage::Welcome => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Welcome,
+ data: ConnCallbackMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionCallbackMessage::Err => {
+ ConnCallbackMsgC {
+ tag: ConnCallbackMsgCTag::Err,
+ data: ConnCallbackMsgCUnion { nul: null_mut() }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core_c/src/data_c/connection_message_c.rs b/core_c/src/data_c/connection_message_c.rs
new file mode 100644
index 0000000..14a49ac
--- /dev/null
+++ b/core_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 ConnMsgC {
+ tag: ConnMsgCTag,
+ 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<ConnMsgC> for ConnectionMessage {
+ fn from(mut value: ConnMsgC) -> 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 ConnMsgC {
+ fn from(value: ConnectionMessage) -> Self {
+ match value {
+ ConnectionMessage::Connection(info) => {
+ ConnMsgC {
+ tag: Connection,
+ data: ConnMsgCUnion { info: ManuallyDrop::new(info) }
+ }
+ }
+ ConnectionMessage::RequestProfile => {
+ ConnMsgC {
+ tag: RequestProfile,
+ data: ConnMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionMessage::RequestLayoutConfigure => {
+ ConnMsgC {
+ tag: RequestLayoutConfigure,
+ data: ConnMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionMessage::RequestSkinPackage => {
+ ConnMsgC {
+ tag: RequestSkinPackage,
+ data: ConnMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionMessage::Ready => {
+ ConnMsgC {
+ tag: Ready,
+ data: ConnMsgCUnion { nul: null_mut() }
+ }
+ }
+ ConnectionMessage::Err => {
+ ConnMsgC {
+ tag: ConnMsgCTag::Err,
+ data: ConnMsgCUnion { nul: null_mut() }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core_c/src/data_c/control_message_c.rs b/core_c/src/data_c/control_message_c.rs
new file mode 100644
index 0000000..41a3e09
--- /dev/null
+++ b/core_c/src/data_c/control_message_c.rs
@@ -0,0 +1,150 @@
+use crate::data_c::common::KeyData;
+use crate::{c_char_to_string_safe, string_to_c_char_safe};
+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 CtrlMsgC {
+ tag: CtrlMsgCTag,
+ 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<CtrlMsgC> for ControlMessage {
+ fn from(msg_c: CtrlMsgC) -> Self {
+ match msg_c.tag {
+ CtrlMsgCTag::Msg => unsafe {
+ let str = c_char_to_string_safe(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 CtrlMsgC {
+ fn from(msg_rs: ControlMessage) -> Self {
+ match msg_rs {
+ ControlMessage::Msg(str_msg) => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Msg,
+ data: CtrlMsgCUnion {
+ str: StrData { str: string_to_c_char_safe(str_msg).1 },
+ }
+ }
+ }
+ ControlMessage::Pressed(key) => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Pressed,
+ data: CtrlMsgCUnion {
+ key: KeyData { key },
+ }
+ }
+ }
+ ControlMessage::Released(key) => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Released,
+ data: CtrlMsgCUnion {
+ key: KeyData { key },
+ }
+ }
+ }
+ ControlMessage::Axis(key, ax) => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Axis,
+ data: CtrlMsgCUnion {
+ axis: AxisData { key, ax }
+ }
+ }
+ }
+ ControlMessage::Dir(key, (x, y)) => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Dir,
+ data: CtrlMsgCUnion {
+ dir: DirData { key, x, y }
+ }
+ }
+ }
+ ControlMessage::Exit => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Exit,
+ data: CtrlMsgCUnion {
+ nul: null_mut(),
+ }
+ }
+ }
+ ControlMessage::Err => {
+ CtrlMsgC {
+ tag: CtrlMsgCTag::Err,
+ data: CtrlMsgCUnion {
+ nul: null_mut(),
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core_c/src/data_c/game_message_c.rs b/core_c/src/data_c/game_message_c.rs
new file mode 100644
index 0000000..8b275ff
--- /dev/null
+++ b/core_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 GameMsgC {
+ tag: GameMsgCTag,
+ 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<GameMsgC> for GameMessage {
+ fn from(msg: GameMsgC) -> 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 GameMsgC {
+ fn from(msg: GameMessage) -> Self {
+ match msg {
+ GameMessage::SkinEventTrigger(key) => {
+ GameMsgC {
+ tag: GameMsgCTag::SkinEventTrigger,
+ data: GameMsgCUnion { key: KeyData { key } }
+ }
+ }
+ GameMessage::DisableKey(key) => {
+ GameMsgC {
+ tag: GameMsgCTag::DisableKey,
+ data: GameMsgCUnion { key: KeyData { key } }
+ }
+ }
+ GameMessage::EnableKey(key) => {
+ GameMsgC {
+ tag: GameMsgCTag::EnableKey,
+ data: GameMsgCUnion { key: KeyData { key } }
+ }
+ }
+ GameMessage::Leave(reason) => {
+ GameMsgC {
+ tag: GameMsgCTag::Leave,
+ data: GameMsgCUnion { reason: reason.into() }
+ }
+ }
+ GameMessage::Err => {
+ GameMsgC {
+ tag: GameMsgCTag::Err,
+ data: GameMsgCUnion { nul: null_mut() }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core_c/src/data_c/game_profile_c.rs b/core_c/src/data_c/game_profile_c.rs
new file mode 100644
index 0000000..199329a
--- /dev/null
+++ b/core_c/src/data_c/game_profile_c.rs
@@ -0,0 +1,44 @@
+use std::ffi::c_char;
+use crate::{box_into_raw, c_char_to_string_safe};
+use nogamepads_lib_rs::pad_data::game_profile::game_profile::GameProfile;
+
+#[unsafe(no_mangle)]
+pub extern "C" fn init_game_profile() -> *mut GameProfile {
+ box_into_raw!(GameProfile::default())
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_name(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.game_name(value.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_description(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.game_description(value.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_organization(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.organization(value.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_version(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.version(value.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_website(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.website(value.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_game_profile_email(game_profile: &mut GameProfile, value: *const c_char) {
+ let value = c_char_to_string_safe(value).unwrap_or_default();
+ game_profile.email(value.as_str());
+} \ No newline at end of file
diff --git a/core_c/src/data_c/mod.rs b/core_c/src/data_c/mod.rs
new file mode 100644
index 0000000..afe8728
--- /dev/null
+++ b/core_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/core_c/src/data_c/player_info_c.rs b/core_c/src/data_c/player_info_c.rs
new file mode 100644
index 0000000..4252b49
--- /dev/null
+++ b/core_c/src/data_c/player_info_c.rs
@@ -0,0 +1,33 @@
+use std::ffi::c_char;
+use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo;
+use crate::{box_into_raw, c_char_to_string_safe};
+
+#[unsafe(no_mangle)]
+pub extern "C" fn init_player_info() -> *mut PlayerInfo {
+ box_into_raw!(PlayerInfo::new())
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_player_info_account(
+ info: &mut PlayerInfo,
+ id: *const c_char,
+ password: *const c_char) {
+ let id = c_char_to_string_safe(id).unwrap_or("".to_string());
+ let password = c_char_to_string_safe(password).unwrap_or("".to_string());
+ info.setup_account_info(id.as_str(), password.as_str());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_player_info_color(
+ info: &mut PlayerInfo,
+ h: i32, s: f64, v: f64) {
+ info.set_customize_color_hsv(h, s, v);
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_player_info_nickname(
+ info: &mut PlayerInfo,
+ nickname: *const c_char) {
+ let nickname = c_char_to_string_safe(nickname).unwrap_or("".to_string());
+ info.set_nickname(nickname.as_str());
+} \ No newline at end of file
diff --git a/core_c/src/data_c/player_info_list_c.rs b/core_c/src/data_c/player_info_list_c.rs
new file mode 100644
index 0000000..9ad8918
--- /dev/null
+++ b/core_c/src/data_c/player_info_list_c.rs
@@ -0,0 +1,8 @@
+use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo;
+
+#[repr(C)]
+pub struct PlayerList {
+ pub(crate) players: *mut PlayerInfo,
+ pub(crate) len: usize,
+ pub(crate) capacity: usize,
+} \ No newline at end of file
diff --git a/core_c/src/service_c/common.rs b/core_c/src/service_c/common.rs
new file mode 100644
index 0000000..8b13da0
--- /dev/null
+++ b/core_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/core_c/src/service_c/mod.rs b/core_c/src/service_c/mod.rs
new file mode 100644
index 0000000..c3d23af
--- /dev/null
+++ b/core_c/src/service_c/mod.rs
@@ -0,0 +1,4 @@
+pub mod common;
+
+pub mod padclient;
+mod padserver; \ No newline at end of file
diff --git a/core_c/src/service_c/padclient.rs b/core_c/src/service_c/padclient.rs
new file mode 100644
index 0000000..77e7d87
--- /dev/null
+++ b/core_c/src/service_c/padclient.rs
@@ -0,0 +1,73 @@
+use std::ffi::c_char;
+use std::net::{IpAddr, Ipv4Addr};
+use std::str::FromStr;
+use nogamepads_lib_rs::DEFAULT_PORT;
+use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::GameMessage;
+use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo;
+use nogamepads_lib_rs::pad_service::client::nogamepads_client::PadClient;
+use crate::{box_into_raw, c_char_to_string_safe};
+use crate::data_c::control_message_c::CtrlMsgC;
+use crate::data_c::game_message_c::GameMsgC;
+
+// 构建
+
+#[unsafe(no_mangle)]
+pub extern "C" fn init_client(address: *const c_char) -> *mut PadClient {
+ let addr_str = c_char_to_string_safe(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));
+ box_into_raw!(PadClient::bind_addr_with_port(IpAddr::from(addr_v4), DEFAULT_PORT))
+}
+
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn init_client_with_port(address: *const c_char, port: u16) -> *mut PadClient {
+ let addr_str = c_char_to_string_safe(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));
+ box_into_raw!(PadClient::bind_addr_with_port(IpAddr::from(addr_v4), port))
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn set_client_quiet(client: &mut PadClient) {
+ client.quiet();
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn enable_client_console(client: &mut PadClient) {
+ client.enable_console();
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn bind_player_to_client(client: &mut PadClient, info: &mut PlayerInfo) {
+ client.bind_player(info.clone());
+}
+
+// 状态控制
+
+#[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: CtrlMsgC) {
+ client.put_msg(msg.into())
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn pop_a_msg_from_server(client: &PadClient) -> GameMsgC {
+ client.pop_a_msg().unwrap_or(GameMessage::Err).into()
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn pop_msg_from_server_or(client: &PadClient, or: GameMsgC) -> GameMsgC {
+ client.pop_msg_or(or.into()).into()
+} \ No newline at end of file
diff --git a/core_c/src/service_c/padserver.rs b/core_c/src/service_c/padserver.rs
new file mode 100644
index 0000000..d0e3267
--- /dev/null
+++ b/core_c/src/service_c/padserver.rs
@@ -0,0 +1,175 @@
+use std::collections::HashMap;
+use crate::{box_into_raw, c_char_to_string_safe};
+use nogamepads_lib_rs::pad_data::game_profile::game_profile::GameProfile;
+use nogamepads_lib_rs::pad_service::server::nogamepads_server::PadServer;
+use nogamepads_lib_rs::DEFAULT_PORT;
+use std::ffi::c_char;
+use std::net::{IpAddr, Ipv4Addr};
+use std::str::FromStr;
+use std::sync::{Arc, MutexGuard, PoisonError};
+use nogamepads_lib_rs::pad_data::pad_messages::nogamepads_messages::ControlMessage;
+use nogamepads_lib_rs::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo;
+use crate::data_c::control_message_c::CtrlMsgC;
+use crate::data_c::game_message_c::GameMsgC;
+use crate::data_c::player_info_list_c::PlayerList;
+
+// 构建
+
+#[unsafe(no_mangle)]
+pub extern "C" fn init_server(address: *const c_char) -> *mut PadServer {
+ let addr_str = c_char_to_string_safe(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 = c_char_to_string_safe(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: &mut GameProfile) {
+ server.put_profile(profile.clone());
+}
+
+// 状态控制
+
+#[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: GameMsgC, player: &PlayerInfo) {
+ let server = unsafe { Box::from_raw(server) };
+ server.put_msg_to(msg.into(), player);
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn put_msg_to_all_players(server: *mut PadServer, msg: GameMsgC) {
+ let server = unsafe { Box::from_raw(server) };
+ server.put_msg_to_all(&msg.into());
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn pop_a_msg_from_player(server: *mut PadServer, player: &PlayerInfo) -> CtrlMsgC {
+ let server = unsafe { Box::from_raw(server) };
+ server.pop_a_msg(player).unwrap_or(ControlMessage::Err).into()
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn pop_msg_from_player_or(server: *mut PadServer, player: &PlayerInfo, or: CtrlMsgC) -> CtrlMsgC {
+ let server = unsafe { Box::from_raw(server) };
+ let msg = server.pop_msg_or(player, or.into());
+ msg.into()
+}
+
+// 玩家管理
+#[unsafe(no_mangle)]
+pub extern "C" fn is_player_online(server: *mut PadServer, player: &PlayerInfo) -> bool {
+ let server = unsafe { Box::from_raw(server) };
+ server.is_player_online(player)
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn is_player_banned(server: *mut PadServer, player: &PlayerInfo) -> bool {
+ let server = unsafe { Box::from_raw(server) };
+ server.is_player_banned(player)
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn kick_player(server: *mut PadServer, player: &PlayerInfo) {
+ let server = unsafe { Box::from_raw(server) };
+ server.kick_player(player);
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn ban_player(server: *mut PadServer, player: &PlayerInfo) {
+ let server = unsafe { Box::from_raw(server) };
+ server.ban_player(player);
+}
+
+#[unsafe(no_mangle)]
+pub extern "C" fn pardon_player(server: *mut PadServer, player: &PlayerInfo) {
+ let server = unsafe { Box::from_raw(server) };
+ server.pardon_player(player);
+}
+
+#[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 vec = result.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