1use std::ffi::{CStr, CString};
98use std::marker::PhantomData;
99use std::ptr::{null, null_mut, NonNull};
100use std::sync::OnceLock;
101
102pub use eos_sys as sys;
103
104#[derive(thiserror::Error, Debug)]
105pub enum Error {
106 #[error("EOS error: {0:?}")]
107 Eos(sys::EOS_EResult),
108 #[error("nul byte in string")]
109 Nul(#[from] std::ffi::NulError),
110 #[error("null pointer from EOS SDK")]
111 Null,
112}
113
114pub type Result<T> = std::result::Result<T, Error>;
115
116fn ok(res: sys::EOS_EResult) -> Result<()> {
117 if res == sys::EOS_EResult_EOS_Success {
118 Ok(())
119 } else {
120 Err(Error::Eos(res))
121 }
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub enum LoginStatus {
126 NotLoggedIn,
127 UsingLocalProfile,
128 LoggedIn,
129 Other(sys::EOS_ELoginStatus),
130}
131
132impl LoginStatus {
133 fn from_raw(status: sys::EOS_ELoginStatus) -> Self {
134 match status {
135 x if x == sys::EOS_ELoginStatus_EOS_LS_NotLoggedIn => Self::NotLoggedIn,
136 x if x == sys::EOS_ELoginStatus_EOS_LS_UsingLocalProfile => Self::UsingLocalProfile,
137 x if x == sys::EOS_ELoginStatus_EOS_LS_LoggedIn => Self::LoggedIn,
138 x => Self::Other(x),
139 }
140 }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
144pub enum NatType {
145 Unknown,
146 Open,
147 Moderate,
148 Strict,
149 Other(sys::EOS_ENATType),
150}
151
152impl NatType {
153 fn from_raw(v: sys::EOS_ENATType) -> Self {
154 match v {
155 x if x == sys::EOS_ENATType_EOS_NAT_Unknown => Self::Unknown,
156 x if x == sys::EOS_ENATType_EOS_NAT_Open => Self::Open,
157 x if x == sys::EOS_ENATType_EOS_NAT_Moderate => Self::Moderate,
158 x if x == sys::EOS_ENATType_EOS_NAT_Strict => Self::Strict,
159 x => Self::Other(x),
160 }
161 }
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub enum RelayControl {
166 NoRelays,
167 AllowRelays,
168 ForceRelays,
169 Other(sys::EOS_ERelayControl),
170}
171
172impl RelayControl {
173 fn from_raw(v: sys::EOS_ERelayControl) -> Self {
174 match v {
175 x if x == sys::EOS_ERelayControl_EOS_RC_NoRelays => Self::NoRelays,
176 x if x == sys::EOS_ERelayControl_EOS_RC_AllowRelays => Self::AllowRelays,
177 x if x == sys::EOS_ERelayControl_EOS_RC_ForceRelays => Self::ForceRelays,
178 x => Self::Other(x),
179 }
180 }
181
182 fn to_raw(self) -> sys::EOS_ERelayControl {
183 match self {
184 Self::NoRelays => sys::EOS_ERelayControl_EOS_RC_NoRelays,
185 Self::AllowRelays => sys::EOS_ERelayControl_EOS_RC_AllowRelays,
186 Self::ForceRelays => sys::EOS_ERelayControl_EOS_RC_ForceRelays,
187 Self::Other(v) => v,
188 }
189 }
190}
191
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193pub enum PacketReliability {
194 UnreliableUnordered,
195 ReliableUnordered,
196 ReliableOrdered,
197}
198
199impl PacketReliability {
200 fn to_raw(self) -> sys::EOS_EPacketReliability {
201 match self {
202 Self::UnreliableUnordered => sys::EOS_EPacketReliability_EOS_PR_UnreliableUnordered,
203 Self::ReliableUnordered => sys::EOS_EPacketReliability_EOS_PR_ReliableUnordered,
204 Self::ReliableOrdered => sys::EOS_EPacketReliability_EOS_PR_ReliableOrdered,
205 }
206 }
207}
208
209#[derive(Clone, Debug)]
210pub struct PacketQueueInfo {
211 pub incoming_max_size_bytes: u64,
212 pub incoming_current_size_bytes: u64,
213 pub incoming_current_packet_count: u64,
214 pub outgoing_max_size_bytes: u64,
215 pub outgoing_current_size_bytes: u64,
216 pub outgoing_current_packet_count: u64,
217}
218
219#[derive(Clone, Debug)]
220pub enum LobbySearchValue {
221 Bool(bool),
222 Int64(i64),
223 Double(f64),
224 String(String),
225}
226
227#[derive(Clone, Debug)]
228pub struct ReceivedPacket {
229 pub peer_id: ProductUserId,
230 pub socket_name: String,
231 pub channel: u8,
232 pub data: Vec<u8>,
233}
234
235pub fn result_to_string(result: sys::EOS_EResult) -> &'static str {
236 let ptr = unsafe { sys::EOS_EResult_ToString(result) };
238 if ptr.is_null() {
239 return "EOS_Unknown";
240 }
241 unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or("EOS_InvalidUtf8")
242}
243
244fn make_socket_id(socket_name: &str) -> Result<sys::EOS_P2P_SocketId> {
245 let c = CString::new(socket_name)?;
246 let bytes = c.as_bytes_with_nul();
247 if bytes.len() > sys::EOS_P2P_SOCKETID_SOCKETNAME_SIZE as usize {
248 return Err(Error::Null);
249 }
250 let mut out = sys::EOS_P2P_SocketId {
251 ApiVersion: sys::EOS_P2P_SOCKETID_API_LATEST as i32,
252 SocketName: [0; sys::EOS_P2P_SOCKETID_SOCKETNAME_SIZE as usize],
253 };
254 for (idx, b) in bytes.iter().copied().enumerate() {
255 out.SocketName[idx] = b as i8;
256 }
257 Ok(out)
258}
259
260fn socket_name_from_raw(socket: &sys::EOS_P2P_SocketId) -> String {
261 let ptr = socket.SocketName.as_ptr();
262 unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
263}
264
265static INITIALIZED: OnceLock<()> = OnceLock::new();
266
267pub struct InitializeOptions {
268 pub product_name: String,
269 pub product_version: String,
270}
271
272pub fn initialize(opts: InitializeOptions) -> Result<()> {
273 if INITIALIZED.get().is_some() {
274 return Ok(());
275 }
276
277 let product_name = CString::new(opts.product_name)?;
278 let product_version = CString::new(opts.product_version)?;
279
280 let init_opts = sys::EOS_InitializeOptions {
281 ApiVersion: sys::EOS_INITIALIZE_API_LATEST as i32,
282 AllocateMemoryFunction: None,
283 ReallocateMemoryFunction: None,
284 ReleaseMemoryFunction: None,
285 ProductName: product_name.as_ptr(),
286 ProductVersion: product_version.as_ptr(),
287 Reserved: null_mut(),
288 SystemInitializeOptions: null_mut(),
289 OverrideThreadAffinity: null_mut(),
290 };
291
292 let res = unsafe { sys::EOS_Initialize(&init_opts) };
294 ok(res)?;
295 let _ = INITIALIZED.set(());
296 Ok(())
297}
298
299pub fn shutdown() -> Result<()> {
300 if INITIALIZED.get().is_none() {
301 return Ok(());
302 }
303 let res = unsafe { sys::EOS_Shutdown() };
304 ok(res)
305}
306
307#[derive(Clone, Copy, Debug)]
308pub struct EpicAccountId(sys::EOS_EpicAccountId);
309
310#[derive(Clone, Copy, Debug)]
311pub struct ProductUserId(sys::EOS_ProductUserId);
312
313#[derive(Clone, Copy, Debug)]
314pub struct ContinuanceToken(sys::EOS_ContinuanceToken);
315
316impl ContinuanceToken {
317 pub fn raw(self) -> sys::EOS_ContinuanceToken {
318 self.0
319 }
320
321 pub fn from_login_callback(info: &sys::EOS_Connect_LoginCallbackInfo) -> Option<Self> {
322 if info.ContinuanceToken.is_null() {
323 None
324 } else {
325 Some(Self(info.ContinuanceToken))
326 }
327 }
328}
329
330#[derive(Clone, Debug)]
331pub struct CreateLobbyParams {
332 pub max_lobby_members: u32,
333 pub permission_level: sys::EOS_ELobbyPermissionLevel,
334 pub presence_enabled: bool,
335 pub allow_invites: bool,
336 pub bucket_id: String,
337 pub disable_host_migration: bool,
338 pub enable_rtc_room: bool,
339 pub enable_join_by_id: bool,
340 pub rejoin_after_kick_requires_invite: bool,
341}
342
343impl Default for CreateLobbyParams {
344 fn default() -> Self {
345 Self {
346 max_lobby_members: 8,
347 permission_level: sys::EOS_ELobbyPermissionLevel_EOS_LPL_PUBLICADVERTISED,
348 presence_enabled: true,
349 allow_invites: true,
350 bucket_id: "default".to_string(),
351 disable_host_migration: false,
352 enable_rtc_room: false,
353 enable_join_by_id: false,
354 rejoin_after_kick_requires_invite: false,
355 }
356 }
357}
358
359impl EpicAccountId {
360 pub fn from_string(s: &str) -> Result<Self> {
361 let s = CString::new(s)?;
362 let raw = unsafe { sys::EOS_EpicAccountId_FromString(s.as_ptr()) };
363 let id = Self(raw);
364 if id.is_valid() {
365 Ok(id)
366 } else {
367 Err(Error::Null)
368 }
369 }
370
371 pub fn to_string(self) -> Result<String> {
372 let mut buf = vec![0i8; (sys::EOS_EPICACCOUNTID_MAX_LENGTH + 1) as usize];
373 let mut len = buf.len() as i32;
374 let res = unsafe { sys::EOS_EpicAccountId_ToString(self.0, buf.as_mut_ptr(), &mut len) };
375 ok(res)?;
376 let s = unsafe { CStr::from_ptr(buf.as_ptr()) }
377 .to_string_lossy()
378 .into_owned();
379 Ok(s)
380 }
381
382 pub fn is_valid(self) -> bool {
383 unsafe { sys::EOS_EpicAccountId_IsValid(self.0) != 0 }
384 }
385
386 pub fn raw(self) -> sys::EOS_EpicAccountId {
387 self.0
388 }
389}
390
391impl ProductUserId {
392 pub fn from_string(s: &str) -> Result<Self> {
393 let s = CString::new(s)?;
394 let raw = unsafe { sys::EOS_ProductUserId_FromString(s.as_ptr()) };
395 let id = Self(raw);
396 if id.is_valid() {
397 Ok(id)
398 } else {
399 Err(Error::Null)
400 }
401 }
402
403 pub fn to_string(self) -> Result<String> {
404 let mut buf = vec![0i8; (sys::EOS_PRODUCTUSERID_MAX_LENGTH + 1) as usize];
405 let mut len = buf.len() as i32;
406 let res = unsafe { sys::EOS_ProductUserId_ToString(self.0, buf.as_mut_ptr(), &mut len) };
407 ok(res)?;
408 let s = unsafe { CStr::from_ptr(buf.as_ptr()) }
409 .to_string_lossy()
410 .into_owned();
411 Ok(s)
412 }
413
414 pub fn is_valid(self) -> bool {
415 unsafe { sys::EOS_ProductUserId_IsValid(self.0) != 0 }
416 }
417
418 pub fn raw(self) -> sys::EOS_ProductUserId {
419 self.0
420 }
421}
422
423pub struct Platform(NonNull<sys::EOS_PlatformHandle>);
424
425unsafe impl Send for Platform {}
426unsafe impl Sync for Platform {}
427
428pub struct PlatformOptions {
429 pub product_id: String,
430 pub sandbox_id: String,
431 pub deployment_id: String,
432 pub client_id: String,
433 pub client_secret: String,
434 pub is_server: bool,
435 pub encryption_key: Option<String>,
436 pub override_country_code: Option<String>,
437 pub override_locale_code: Option<String>,
438}
439
440impl Platform {
441 pub fn create(opts: PlatformOptions) -> Result<Self> {
442 let product_id = CString::new(opts.product_id)?;
443 let sandbox_id = CString::new(opts.sandbox_id)?;
444 let deployment_id = CString::new(opts.deployment_id)?;
445 let client_id = CString::new(opts.client_id)?;
446 let client_secret = CString::new(opts.client_secret)?;
447 let encryption_key = match opts.encryption_key {
448 Some(s) => Some(CString::new(s)?),
449 None => None,
450 };
451 let override_country_code = match opts.override_country_code {
452 Some(s) => Some(CString::new(s)?),
453 None => None,
454 };
455 let override_locale_code = match opts.override_locale_code {
456 Some(s) => Some(CString::new(s)?),
457 None => None,
458 };
459
460 let options = sys::EOS_Platform_Options {
461 ApiVersion: sys::EOS_PLATFORM_OPTIONS_API_LATEST as i32,
462 Reserved: null_mut(),
463 bIsServer: if opts.is_server { 1 } else { 0 },
464 EncryptionKey: encryption_key.as_ref().map(|s| s.as_ptr()).unwrap_or(std::ptr::null()),
465 OverrideCountryCode: override_country_code
466 .as_ref()
467 .map(|s| s.as_ptr())
468 .unwrap_or(std::ptr::null()),
469 OverrideLocaleCode: override_locale_code
470 .as_ref()
471 .map(|s| s.as_ptr())
472 .unwrap_or(std::ptr::null()),
473 ProductId: product_id.as_ptr(),
474 SandboxId: sandbox_id.as_ptr(),
475 DeploymentId: deployment_id.as_ptr(),
476 ClientCredentials: sys::EOS_Platform_ClientCredentials {
477 ClientId: client_id.as_ptr(),
478 ClientSecret: client_secret.as_ptr(),
479 },
480 Flags: 0,
481 CacheDirectory: std::ptr::null(),
482 TickBudgetInMilliseconds: 0,
483 RTCOptions: std::ptr::null(),
484 IntegratedPlatformOptionsContainerHandle: null_mut(),
485 SystemSpecificOptions: null(),
486 TaskNetworkTimeoutSeconds: null_mut(),
487 };
488
489 let handle = unsafe { sys::EOS_Platform_Create(&options) };
490 let nn = NonNull::new(handle as *mut sys::EOS_PlatformHandle).ok_or(Error::Null)?;
491 Ok(Self(nn))
492 }
493
494 #[inline]
495 fn as_handle(&self) -> sys::EOS_HPlatform {
496 self.0.as_ptr() as sys::EOS_HPlatform
497 }
498
499 pub fn tick(&self) {
500 unsafe { sys::EOS_Platform_Tick(self.as_handle()) };
501 }
502
503 pub fn raw_handle(&self) -> sys::EOS_HPlatform {
504 self.as_handle()
505 }
506
507 pub fn auth(&self) -> Auth {
508 let h = unsafe { sys::EOS_Platform_GetAuthInterface(self.as_handle()) };
509 Auth(NonNull::new(h as *mut sys::EOS_AuthHandle).expect("EOS auth interface null"))
510 }
511
512 pub fn connect(&self) -> Connect {
513 let h = unsafe { sys::EOS_Platform_GetConnectInterface(self.as_handle()) };
514 Connect(NonNull::new(h as *mut sys::EOS_ConnectHandle).expect("EOS connect interface null"))
515 }
516
517 pub fn achievements(&self) -> Achievements {
518 let h = unsafe { sys::EOS_Platform_GetAchievementsInterface(self.as_handle()) };
519 Achievements(
520 NonNull::new(h as *mut sys::EOS_AchievementsHandle)
521 .expect("EOS achievements interface null"),
522 )
523 }
524
525 pub fn anticheat_client(&self) -> AntiCheatClient {
526 let h = unsafe { sys::EOS_Platform_GetAntiCheatClientInterface(self.as_handle()) };
527 AntiCheatClient(
528 NonNull::new(h as *mut sys::EOS_AntiCheatClientHandle)
529 .expect("EOS anticheat client interface null"),
530 )
531 }
532
533 pub fn anticheat_server(&self) -> AntiCheatServer {
534 let h = unsafe { sys::EOS_Platform_GetAntiCheatServerInterface(self.as_handle()) };
535 AntiCheatServer(
536 NonNull::new(h as *mut sys::EOS_AntiCheatServerHandle)
537 .expect("EOS anticheat server interface null"),
538 )
539 }
540
541 pub fn custom_invites(&self) -> CustomInvites {
542 let h = unsafe { sys::EOS_Platform_GetCustomInvitesInterface(self.as_handle()) };
543 CustomInvites(
544 NonNull::new(h as *mut sys::EOS_CustomInvitesHandle)
545 .expect("EOS custom invites interface null"),
546 )
547 }
548
549 pub fn ecom(&self) -> Ecom {
550 let h = unsafe { sys::EOS_Platform_GetEcomInterface(self.as_handle()) };
551 Ecom(NonNull::new(h as *mut sys::EOS_EcomHandle).expect("EOS ecom interface null"))
552 }
553
554 pub fn friends(&self) -> Friends {
555 let h = unsafe { sys::EOS_Platform_GetFriendsInterface(self.as_handle()) };
556 Friends(NonNull::new(h as *mut sys::EOS_FriendsHandle).expect("EOS friends interface null"))
557 }
558
559 pub fn integrated_platform(&self) -> IntegratedPlatform {
560 let h = unsafe { sys::EOS_Platform_GetIntegratedPlatformInterface(self.as_handle()) };
561 IntegratedPlatform(
562 NonNull::new(h as *mut sys::EOS_IntegratedPlatformHandle)
563 .expect("EOS integrated platform interface null"),
564 )
565 }
566
567 pub fn kws(&self) -> Kws {
568 let h = unsafe { sys::EOS_Platform_GetKWSInterface(self.as_handle()) };
569 Kws(NonNull::new(h as *mut sys::EOS_KWSHandle).expect("EOS KWS interface null"))
570 }
571
572 pub fn leaderboards(&self) -> Leaderboards {
573 let h = unsafe { sys::EOS_Platform_GetLeaderboardsInterface(self.as_handle()) };
574 Leaderboards(
575 NonNull::new(h as *mut sys::EOS_LeaderboardsHandle)
576 .expect("EOS leaderboards interface null"),
577 )
578 }
579
580 pub fn lobby(&self) -> Lobby {
581 let h = unsafe { sys::EOS_Platform_GetLobbyInterface(self.as_handle()) };
582 Lobby(NonNull::new(h as *mut sys::EOS_LobbyHandle).expect("EOS lobby interface null"))
583 }
584
585 pub fn metrics(&self) -> Metrics {
586 let h = unsafe { sys::EOS_Platform_GetMetricsInterface(self.as_handle()) };
587 Metrics(NonNull::new(h as *mut sys::EOS_MetricsHandle).expect("EOS metrics interface null"))
588 }
589
590 pub fn mods(&self) -> Mods {
591 let h = unsafe { sys::EOS_Platform_GetModsInterface(self.as_handle()) };
592 Mods(NonNull::new(h as *mut sys::EOS_ModsHandle).expect("EOS mods interface null"))
593 }
594
595 pub fn p2p(&self) -> P2P {
596 let h = unsafe { sys::EOS_Platform_GetP2PInterface(self.as_handle()) };
597 P2P(NonNull::new(h as *mut sys::EOS_P2PHandle).expect("EOS p2p interface null"))
598 }
599
600 pub fn player_data_storage(&self) -> PlayerDataStorage {
601 let h = unsafe { sys::EOS_Platform_GetPlayerDataStorageInterface(self.as_handle()) };
602 PlayerDataStorage(
603 NonNull::new(h as *mut sys::EOS_PlayerDataStorageHandle)
604 .expect("EOS player data storage interface null"),
605 )
606 }
607
608 pub fn presence(&self) -> Presence {
609 let h = unsafe { sys::EOS_Platform_GetPresenceInterface(self.as_handle()) };
610 Presence(
611 NonNull::new(h as *mut sys::EOS_PresenceHandle).expect("EOS presence interface null"),
612 )
613 }
614
615 pub fn progressionsnapshot(&self) -> ProgressionSnapshot {
616 let h = unsafe { sys::EOS_Platform_GetProgressionSnapshotInterface(self.as_handle()) };
617 ProgressionSnapshot(
618 NonNull::new(h as *mut sys::EOS_ProgressionSnapshotHandle)
619 .expect("EOS progression snapshot interface null"),
620 )
621 }
622
623 pub fn reports(&self) -> Reports {
624 let h = unsafe { sys::EOS_Platform_GetReportsInterface(self.as_handle()) };
625 Reports(
626 NonNull::new(h as *mut sys::EOS_ReportsHandle).expect("EOS reports interface null"),
627 )
628 }
629
630 pub fn rtc(&self) -> Rtc {
631 let h = unsafe { sys::EOS_Platform_GetRTCInterface(self.as_handle()) };
632 Rtc(NonNull::new(h as *mut sys::EOS_RTCHandle).expect("EOS RTC interface null"))
633 }
634
635 pub fn rtc_admin(&self) -> RtcAdmin {
636 let h = unsafe { sys::EOS_Platform_GetRTCAdminInterface(self.as_handle()) };
637 RtcAdmin(
638 NonNull::new(h as *mut sys::EOS_RTCAdminHandle).expect("EOS RTC admin interface null"),
639 )
640 }
641
642 pub fn sanctions(&self) -> Sanctions {
643 let h = unsafe { sys::EOS_Platform_GetSanctionsInterface(self.as_handle()) };
644 Sanctions(
645 NonNull::new(h as *mut sys::EOS_SanctionsHandle).expect("EOS sanctions interface null"),
646 )
647 }
648
649 pub fn sessions(&self) -> Sessions {
650 let h = unsafe { sys::EOS_Platform_GetSessionsInterface(self.as_handle()) };
651 Sessions(
652 NonNull::new(h as *mut sys::EOS_SessionsHandle).expect("EOS sessions interface null"),
653 )
654 }
655
656 pub fn stats(&self) -> Stats {
657 let h = unsafe { sys::EOS_Platform_GetStatsInterface(self.as_handle()) };
658 Stats(NonNull::new(h as *mut sys::EOS_StatsHandle).expect("EOS stats interface null"))
659 }
660
661 pub fn title_storage(&self) -> TitleStorage {
662 let h = unsafe { sys::EOS_Platform_GetTitleStorageInterface(self.as_handle()) };
663 TitleStorage(
664 NonNull::new(h as *mut sys::EOS_TitleStorageHandle)
665 .expect("EOS title storage interface null"),
666 )
667 }
668
669 pub fn ui(&self) -> Ui {
670 let h = unsafe { sys::EOS_Platform_GetUIInterface(self.as_handle()) };
671 Ui(NonNull::new(h as *mut sys::EOS_UIHandle).expect("EOS UI interface null"))
672 }
673
674 pub fn userinfo(&self) -> UserInfo {
675 let h = unsafe { sys::EOS_Platform_GetUserInfoInterface(self.as_handle()) };
676 UserInfo(
677 NonNull::new(h as *mut sys::EOS_UserInfoHandle).expect("EOS userinfo interface null"),
678 )
679 }
680}
681
682impl Drop for Platform {
683 fn drop(&mut self) {
684 unsafe { sys::EOS_Platform_Release(self.as_handle()) };
685 }
686}
687
688pub struct Auth(NonNull<sys::EOS_AuthHandle>);
689pub struct Connect(NonNull<sys::EOS_ConnectHandle>);
690pub struct Achievements(NonNull<sys::EOS_AchievementsHandle>);
691pub struct AntiCheatClient(NonNull<sys::EOS_AntiCheatClientHandle>);
692pub struct AntiCheatServer(NonNull<sys::EOS_AntiCheatServerHandle>);
693pub struct CustomInvites(NonNull<sys::EOS_CustomInvitesHandle>);
694pub struct Ecom(NonNull<sys::EOS_EcomHandle>);
695pub struct Friends(NonNull<sys::EOS_FriendsHandle>);
696pub struct IntegratedPlatform(NonNull<sys::EOS_IntegratedPlatformHandle>);
697pub struct Kws(NonNull<sys::EOS_KWSHandle>);
698pub struct Leaderboards(NonNull<sys::EOS_LeaderboardsHandle>);
699pub struct Lobby(NonNull<sys::EOS_LobbyHandle>);
700pub struct Metrics(NonNull<sys::EOS_MetricsHandle>);
701pub struct Mods(NonNull<sys::EOS_ModsHandle>);
702pub struct P2P(NonNull<sys::EOS_P2PHandle>);
703pub struct PlayerDataStorage(NonNull<sys::EOS_PlayerDataStorageHandle>);
704pub struct Presence(NonNull<sys::EOS_PresenceHandle>);
705pub struct ProgressionSnapshot(NonNull<sys::EOS_ProgressionSnapshotHandle>);
706pub struct Reports(NonNull<sys::EOS_ReportsHandle>);
707pub struct Rtc(NonNull<sys::EOS_RTCHandle>);
708pub struct RtcAdmin(NonNull<sys::EOS_RTCAdminHandle>);
709pub struct Sanctions(NonNull<sys::EOS_SanctionsHandle>);
710pub struct Sessions(NonNull<sys::EOS_SessionsHandle>);
711pub struct Stats(NonNull<sys::EOS_StatsHandle>);
712pub struct TitleStorage(NonNull<sys::EOS_TitleStorageHandle>);
713pub struct Ui(NonNull<sys::EOS_UIHandle>);
714pub struct UserInfo(NonNull<sys::EOS_UserInfoHandle>);
715
716struct CallbackOnce<T> {
718 ptr: *mut T,
719 _marker: PhantomData<T>,
720}
721
722impl<T> CallbackOnce<T> {
723 fn new(val: T) -> Self {
724 let b = Box::new(val);
725 Self {
726 ptr: Box::into_raw(b),
727 _marker: PhantomData,
728 }
729 }
730}
731
732impl Auth {
733 fn as_handle(&self) -> sys::EOS_HAuth {
734 self.0.as_ptr() as sys::EOS_HAuth
735 }
736
737 pub fn raw_handle(&self) -> sys::EOS_HAuth {
738 self.as_handle()
739 }
740
741 pub fn get_login_status(&self, local_user: EpicAccountId) -> LoginStatus {
742 let raw = unsafe { sys::EOS_Auth_GetLoginStatus(self.as_handle(), local_user.raw()) };
743 LoginStatus::from_raw(raw)
744 }
745
746 pub fn copy_user_auth_token(&self, local_user: EpicAccountId) -> Result<AuthToken> {
747 let options = sys::EOS_Auth_CopyUserAuthTokenOptions {
748 ApiVersion: sys::EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST as i32,
749 };
750 let mut token_ptr: *mut sys::EOS_Auth_Token = std::ptr::null_mut();
751 let res = unsafe {
752 sys::EOS_Auth_CopyUserAuthToken(
753 self.as_handle(),
754 &options,
755 local_user.raw(),
756 &mut token_ptr,
757 )
758 };
759 ok(res)?;
760 unsafe { AuthToken::from_raw(token_ptr) }
761 }
762
763 pub fn copy_id_token(&self, account: EpicAccountId) -> Result<AuthIdToken> {
764 let options = sys::EOS_Auth_CopyIdTokenOptions {
765 ApiVersion: sys::EOS_AUTH_COPYIDTOKEN_API_LATEST as i32,
766 AccountId: account.raw(),
767 };
768 let mut token_ptr: *mut sys::EOS_Auth_IdToken = std::ptr::null_mut();
769 let res = unsafe { sys::EOS_Auth_CopyIdToken(self.as_handle(), &options, &mut token_ptr) };
770 ok(res)?;
771 unsafe { AuthIdToken::from_raw(token_ptr) }
772 }
773
774 pub fn query_id_token(
775 &self,
776 local_user: EpicAccountId,
777 target_account: EpicAccountId,
778 cb: impl FnOnce(Result<sys::EOS_Auth_QueryIdTokenCallbackInfo>) + Send + 'static,
779 ) {
780 #[repr(C)]
781 struct Cb {
782 f: Option<Box<dyn FnOnce(Result<sys::EOS_Auth_QueryIdTokenCallbackInfo>) + Send>>,
783 }
784
785 unsafe extern "C" fn trampoline(data: *const sys::EOS_Auth_QueryIdTokenCallbackInfo) {
786 let client_data = (*data).ClientData as *mut Cb;
787 let mut boxed = Box::from_raw(client_data);
788 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
789 Ok(*data)
790 } else {
791 Err(Error::Eos((*data).ResultCode))
792 };
793 if let Some(f) = boxed.f.take() {
794 f(res);
795 }
796 }
797
798 let cb_box = CallbackOnce::new(Cb {
799 f: Some(Box::new(cb)),
800 });
801
802 let options = sys::EOS_Auth_QueryIdTokenOptions {
803 ApiVersion: sys::EOS_AUTH_QUERYIDTOKEN_API_LATEST as i32,
804 LocalUserId: local_user.raw(),
805 TargetAccountId: target_account.raw(),
806 };
807
808 unsafe {
809 sys::EOS_Auth_QueryIdToken(
810 self.as_handle(),
811 &options,
812 cb_box.ptr as *mut _,
813 Some(trampoline),
814 );
815 }
816 }
817
818 pub fn logout(
819 &self,
820 local_user: EpicAccountId,
821 cb: impl FnOnce(Result<sys::EOS_Auth_LogoutCallbackInfo>) + Send + 'static,
822 ) {
823 #[repr(C)]
824 struct Cb {
825 f: Option<Box<dyn FnOnce(Result<sys::EOS_Auth_LogoutCallbackInfo>) + Send>>,
826 }
827
828 unsafe extern "C" fn trampoline(data: *const sys::EOS_Auth_LogoutCallbackInfo) {
829 let client_data = (*data).ClientData as *mut Cb;
830 let mut boxed = Box::from_raw(client_data);
831 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
832 Ok(*data)
833 } else {
834 Err(Error::Eos((*data).ResultCode))
835 };
836 if let Some(f) = boxed.f.take() {
837 f(res);
838 }
839 }
840
841 let cb_box = CallbackOnce::new(Cb {
842 f: Some(Box::new(cb)),
843 });
844
845 let options = sys::EOS_Auth_LogoutOptions {
846 ApiVersion: sys::EOS_AUTH_LOGOUT_API_LATEST as i32,
847 LocalUserId: local_user.raw(),
848 };
849
850 unsafe {
851 sys::EOS_Auth_Logout(
852 self.as_handle(),
853 &options,
854 cb_box.ptr as *mut _,
855 Some(trampoline),
856 );
857 }
858 }
859
860 pub fn login_epic_exchange_code(
861 &self,
862 exchange_code: &str,
863 cb: impl FnOnce(Result<sys::EOS_Auth_LoginCallbackInfo>) + Send + 'static,
864 ) -> Result<()> {
865 let exchange_code = CString::new(exchange_code)?;
866
867 #[repr(C)]
868 struct Cb {
869 f: Option<Box<dyn FnOnce(Result<sys::EOS_Auth_LoginCallbackInfo>) + Send>>,
870 }
871
872 unsafe extern "C" fn trampoline(data: *const sys::EOS_Auth_LoginCallbackInfo) {
873 let client_data = (*data).ClientData as *mut Cb;
874 let mut boxed = Box::from_raw(client_data);
875 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
876 Ok(*data)
877 } else {
878 Err(Error::Eos((*data).ResultCode))
879 };
880 if let Some(f) = boxed.f.take() {
881 f(res);
882 }
883 }
884
885 let cb_box = CallbackOnce::new(Cb {
886 f: Some(Box::new(cb)),
887 });
888
889 let creds = sys::EOS_Auth_Credentials {
890 ApiVersion: sys::EOS_AUTH_CREDENTIALS_API_LATEST as i32,
891 Id: std::ptr::null(),
892 Token: exchange_code.as_ptr(),
893 Type: sys::EOS_ELoginCredentialType_EOS_LCT_ExchangeCode,
894 SystemAuthCredentialsOptions: null_mut(),
895 ExternalType: sys::EOS_EExternalCredentialType_EOS_ECT_EPIC,
896 };
897
898 let options = sys::EOS_Auth_LoginOptions {
899 ApiVersion: sys::EOS_AUTH_LOGIN_API_LATEST as i32,
900 Credentials: &creds,
901 ScopeFlags: 0,
902 LoginFlags: 0,
903 };
904
905 unsafe {
906 sys::EOS_Auth_Login(
907 self.as_handle(),
908 &options,
909 cb_box.ptr as *mut _,
910 Some(trampoline),
911 );
912 }
913 Ok(())
914 }
915}
916
917impl Connect {
918 pub fn raw_handle(&self) -> sys::EOS_HConnect {
919 self.0.as_ptr() as sys::EOS_HConnect
920 }
921
922 pub fn get_login_status(&self, local_user: ProductUserId) -> LoginStatus {
923 let raw = unsafe { sys::EOS_Connect_GetLoginStatus(self.raw_handle(), local_user.raw()) };
924 LoginStatus::from_raw(raw)
925 }
926
927 pub fn copy_id_token(&self, local_user: ProductUserId) -> Result<ConnectIdToken> {
928 let options = sys::EOS_Connect_CopyIdTokenOptions {
929 ApiVersion: sys::EOS_CONNECT_COPYIDTOKEN_API_LATEST as i32,
930 LocalUserId: local_user.raw(),
931 };
932 let mut token_ptr: *mut sys::EOS_Connect_IdToken = std::ptr::null_mut();
933 let res = unsafe { sys::EOS_Connect_CopyIdToken(self.raw_handle(), &options, &mut token_ptr) };
934 ok(res)?;
935 unsafe { ConnectIdToken::from_raw(token_ptr) }
936 }
937
938 pub fn login_openid_access_token(
939 &self,
940 token: &str,
941 display_name: Option<&str>,
942 cb: impl FnOnce(Result<sys::EOS_Connect_LoginCallbackInfo>) + Send + 'static,
943 ) -> Result<()> {
944 let token = CString::new(token)?;
945 let display_name_cstr = match display_name {
946 Some(v) => Some(CString::new(v)?),
947 None => None,
948 };
949
950 #[repr(C)]
951 struct Cb {
952 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_LoginCallbackInfo>) + Send>>,
953 }
954 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_LoginCallbackInfo) {
955 let client_data = (*data).ClientData as *mut Cb;
956 let mut boxed = Box::from_raw(client_data);
957 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
958 Ok(*data)
959 } else {
960 Err(Error::Eos((*data).ResultCode))
961 };
962 if let Some(f) = boxed.f.take() {
963 f(res);
964 }
965 }
966
967 let cb_box = CallbackOnce::new(Cb {
968 f: Some(Box::new(cb)),
969 });
970
971 let credentials = sys::EOS_Connect_Credentials {
972 ApiVersion: sys::EOS_CONNECT_CREDENTIALS_API_LATEST as i32,
973 Token: token.as_ptr(),
974 Type: sys::EOS_EExternalCredentialType_EOS_ECT_OPENID_ACCESS_TOKEN,
975 };
976
977 let user_login_info;
978 let user_login_info_ptr = if let Some(name) = display_name_cstr.as_ref() {
979 user_login_info = sys::EOS_Connect_UserLoginInfo {
980 ApiVersion: sys::EOS_CONNECT_USERLOGININFO_API_LATEST as i32,
981 DisplayName: name.as_ptr(),
982 NsaIdToken: std::ptr::null(),
983 };
984 &user_login_info as *const sys::EOS_Connect_UserLoginInfo
985 } else {
986 std::ptr::null()
987 };
988
989 let options = sys::EOS_Connect_LoginOptions {
990 ApiVersion: sys::EOS_CONNECT_LOGIN_API_LATEST as i32,
991 Credentials: &credentials,
992 UserLoginInfo: user_login_info_ptr,
993 };
994
995 unsafe {
996 sys::EOS_Connect_Login(
997 self.raw_handle(),
998 &options,
999 cb_box.ptr as *mut _,
1000 Some(trampoline),
1001 );
1002 }
1003 Ok(())
1004 }
1005
1006 pub fn create_user(
1007 &self,
1008 continuance_token: ContinuanceToken,
1009 cb: impl FnOnce(Result<sys::EOS_Connect_CreateUserCallbackInfo>) + Send + 'static,
1010 ) {
1011 #[repr(C)]
1012 struct Cb {
1013 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_CreateUserCallbackInfo>) + Send>>,
1014 }
1015 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_CreateUserCallbackInfo) {
1016 let client_data = (*data).ClientData as *mut Cb;
1017 let mut boxed = Box::from_raw(client_data);
1018 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1019 Ok(*data)
1020 } else {
1021 Err(Error::Eos((*data).ResultCode))
1022 };
1023 if let Some(f) = boxed.f.take() {
1024 f(res);
1025 }
1026 }
1027
1028 let cb_box = CallbackOnce::new(Cb {
1029 f: Some(Box::new(cb)),
1030 });
1031
1032 let options = sys::EOS_Connect_CreateUserOptions {
1033 ApiVersion: sys::EOS_CONNECT_CREATEUSER_API_LATEST as i32,
1034 ContinuanceToken: continuance_token.raw(),
1035 };
1036
1037 unsafe {
1038 sys::EOS_Connect_CreateUser(
1039 self.raw_handle(),
1040 &options,
1041 cb_box.ptr as *mut _,
1042 Some(trampoline),
1043 );
1044 }
1045 }
1046
1047 pub fn logout(
1048 &self,
1049 local_user: ProductUserId,
1050 cb: impl FnOnce(Result<sys::EOS_Connect_LogoutCallbackInfo>) + Send + 'static,
1051 ) {
1052 #[repr(C)]
1053 struct Cb {
1054 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_LogoutCallbackInfo>) + Send>>,
1055 }
1056 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_LogoutCallbackInfo) {
1057 let client_data = (*data).ClientData as *mut Cb;
1058 let mut boxed = Box::from_raw(client_data);
1059 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1060 Ok(*data)
1061 } else {
1062 Err(Error::Eos((*data).ResultCode))
1063 };
1064 if let Some(f) = boxed.f.take() {
1065 f(res);
1066 }
1067 }
1068 let cb_box = CallbackOnce::new(Cb {
1069 f: Some(Box::new(cb)),
1070 });
1071 let options = sys::EOS_Connect_LogoutOptions {
1072 ApiVersion: sys::EOS_CONNECT_LOGOUT_API_LATEST as i32,
1073 LocalUserId: local_user.raw(),
1074 };
1075 unsafe {
1076 sys::EOS_Connect_Logout(
1077 self.raw_handle(),
1078 &options,
1079 cb_box.ptr as *mut _,
1080 Some(trampoline),
1081 );
1082 }
1083 }
1084
1085 pub fn link_account(
1086 &self,
1087 local_user: ProductUserId,
1088 continuance_token: ContinuanceToken,
1089 cb: impl FnOnce(Result<sys::EOS_Connect_LinkAccountCallbackInfo>) + Send + 'static,
1090 ) {
1091 #[repr(C)]
1092 struct Cb {
1093 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_LinkAccountCallbackInfo>) + Send>>,
1094 }
1095 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_LinkAccountCallbackInfo) {
1096 let client_data = (*data).ClientData as *mut Cb;
1097 let mut boxed = Box::from_raw(client_data);
1098 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1099 Ok(*data)
1100 } else {
1101 Err(Error::Eos((*data).ResultCode))
1102 };
1103 if let Some(f) = boxed.f.take() {
1104 f(res);
1105 }
1106 }
1107 let cb_box = CallbackOnce::new(Cb {
1108 f: Some(Box::new(cb)),
1109 });
1110 let options = sys::EOS_Connect_LinkAccountOptions {
1111 ApiVersion: sys::EOS_CONNECT_LINKACCOUNT_API_LATEST as i32,
1112 LocalUserId: local_user.raw(),
1113 ContinuanceToken: continuance_token.raw(),
1114 };
1115 unsafe {
1116 sys::EOS_Connect_LinkAccount(
1117 self.raw_handle(),
1118 &options,
1119 cb_box.ptr as *mut _,
1120 Some(trampoline),
1121 );
1122 }
1123 }
1124
1125 pub fn unlink_account(
1126 &self,
1127 local_user: ProductUserId,
1128 cb: impl FnOnce(Result<sys::EOS_Connect_UnlinkAccountCallbackInfo>) + Send + 'static,
1129 ) {
1130 #[repr(C)]
1131 struct Cb {
1132 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_UnlinkAccountCallbackInfo>) + Send>>,
1133 }
1134 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_UnlinkAccountCallbackInfo) {
1135 let client_data = (*data).ClientData as *mut Cb;
1136 let mut boxed = Box::from_raw(client_data);
1137 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1138 Ok(*data)
1139 } else {
1140 Err(Error::Eos((*data).ResultCode))
1141 };
1142 if let Some(f) = boxed.f.take() {
1143 f(res);
1144 }
1145 }
1146 let cb_box = CallbackOnce::new(Cb {
1147 f: Some(Box::new(cb)),
1148 });
1149 let options = sys::EOS_Connect_UnlinkAccountOptions {
1150 ApiVersion: sys::EOS_CONNECT_UNLINKACCOUNT_API_LATEST as i32,
1151 LocalUserId: local_user.raw(),
1152 };
1153 unsafe {
1154 sys::EOS_Connect_UnlinkAccount(
1155 self.raw_handle(),
1156 &options,
1157 cb_box.ptr as *mut _,
1158 Some(trampoline),
1159 );
1160 }
1161 }
1162
1163 pub fn create_device_id(
1164 &self,
1165 device_model: &str,
1166 cb: impl FnOnce(Result<sys::EOS_Connect_CreateDeviceIdCallbackInfo>) + Send + 'static,
1167 ) -> Result<()> {
1168 #[repr(C)]
1169 struct Cb {
1170 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_CreateDeviceIdCallbackInfo>) + Send>>,
1171 }
1172 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_CreateDeviceIdCallbackInfo) {
1173 let client_data = (*data).ClientData as *mut Cb;
1174 let mut boxed = Box::from_raw(client_data);
1175 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1176 Ok(*data)
1177 } else {
1178 Err(Error::Eos((*data).ResultCode))
1179 };
1180 if let Some(f) = boxed.f.take() {
1181 f(res);
1182 }
1183 }
1184 let cb_box = CallbackOnce::new(Cb {
1185 f: Some(Box::new(cb)),
1186 });
1187 let model = CString::new(device_model)?;
1188 let options = sys::EOS_Connect_CreateDeviceIdOptions {
1189 ApiVersion: sys::EOS_CONNECT_CREATEDEVICEID_API_LATEST as i32,
1190 DeviceModel: model.as_ptr(),
1191 };
1192 unsafe {
1193 sys::EOS_Connect_CreateDeviceId(
1194 self.raw_handle(),
1195 &options,
1196 cb_box.ptr as *mut _,
1197 Some(trampoline),
1198 );
1199 }
1200 Ok(())
1201 }
1202
1203 pub fn delete_device_id(
1204 &self,
1205 cb: impl FnOnce(Result<sys::EOS_Connect_DeleteDeviceIdCallbackInfo>) + Send + 'static,
1206 ) {
1207 #[repr(C)]
1208 struct Cb {
1209 f: Option<Box<dyn FnOnce(Result<sys::EOS_Connect_DeleteDeviceIdCallbackInfo>) + Send>>,
1210 }
1211 unsafe extern "C" fn trampoline(data: *const sys::EOS_Connect_DeleteDeviceIdCallbackInfo) {
1212 let client_data = (*data).ClientData as *mut Cb;
1213 let mut boxed = Box::from_raw(client_data);
1214 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1215 Ok(*data)
1216 } else {
1217 Err(Error::Eos((*data).ResultCode))
1218 };
1219 if let Some(f) = boxed.f.take() {
1220 f(res);
1221 }
1222 }
1223 let cb_box = CallbackOnce::new(Cb {
1224 f: Some(Box::new(cb)),
1225 });
1226 let options = sys::EOS_Connect_DeleteDeviceIdOptions {
1227 ApiVersion: sys::EOS_CONNECT_DELETEDEVICEID_API_LATEST as i32,
1228 };
1229 unsafe {
1230 sys::EOS_Connect_DeleteDeviceId(
1231 self.raw_handle(),
1232 &options,
1233 cb_box.ptr as *mut _,
1234 Some(trampoline),
1235 );
1236 }
1237 }
1238
1239 pub fn transfer_device_id_account(
1240 &self,
1241 primary_local_user: ProductUserId,
1242 local_device_user: ProductUserId,
1243 product_user_to_preserve: ProductUserId,
1244 cb: impl FnOnce(Result<sys::EOS_Connect_TransferDeviceIdAccountCallbackInfo>) + Send + 'static,
1245 ) {
1246 #[repr(C)]
1247 struct Cb {
1248 f: Option<
1249 Box<dyn FnOnce(Result<sys::EOS_Connect_TransferDeviceIdAccountCallbackInfo>) + Send>,
1250 >,
1251 }
1252 unsafe extern "C" fn trampoline(
1253 data: *const sys::EOS_Connect_TransferDeviceIdAccountCallbackInfo,
1254 ) {
1255 let client_data = (*data).ClientData as *mut Cb;
1256 let mut boxed = Box::from_raw(client_data);
1257 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1258 Ok(*data)
1259 } else {
1260 Err(Error::Eos((*data).ResultCode))
1261 };
1262 if let Some(f) = boxed.f.take() {
1263 f(res);
1264 }
1265 }
1266 let cb_box = CallbackOnce::new(Cb {
1267 f: Some(Box::new(cb)),
1268 });
1269 let options = sys::EOS_Connect_TransferDeviceIdAccountOptions {
1270 ApiVersion: sys::EOS_CONNECT_TRANSFERDEVICEIDACCOUNT_API_LATEST as i32,
1271 PrimaryLocalUserId: primary_local_user.raw(),
1272 LocalDeviceUserId: local_device_user.raw(),
1273 ProductUserIdToPreserve: product_user_to_preserve.raw(),
1274 };
1275 unsafe {
1276 sys::EOS_Connect_TransferDeviceIdAccount(
1277 self.raw_handle(),
1278 &options,
1279 cb_box.ptr as *mut _,
1280 Some(trampoline),
1281 );
1282 }
1283 }
1284}
1285
1286macro_rules! impl_raw_handle {
1287 ($ty:ident, $raw:ty) => {
1288 impl $ty {
1289 pub fn raw_handle(&self) -> $raw {
1290 self.0.as_ptr() as $raw
1291 }
1292 }
1293 };
1294}
1295
1296impl_raw_handle!(Achievements, sys::EOS_HAchievements);
1297impl_raw_handle!(AntiCheatClient, sys::EOS_HAntiCheatClient);
1298impl_raw_handle!(AntiCheatServer, sys::EOS_HAntiCheatServer);
1299impl_raw_handle!(CustomInvites, sys::EOS_HCustomInvites);
1300impl_raw_handle!(Ecom, sys::EOS_HEcom);
1301impl_raw_handle!(Friends, sys::EOS_HFriends);
1302impl_raw_handle!(IntegratedPlatform, sys::EOS_HIntegratedPlatform);
1303impl_raw_handle!(Kws, sys::EOS_HKWS);
1304impl_raw_handle!(Leaderboards, sys::EOS_HLeaderboards);
1305impl_raw_handle!(Metrics, sys::EOS_HMetrics);
1306impl_raw_handle!(Mods, sys::EOS_HMods);
1307impl_raw_handle!(PlayerDataStorage, sys::EOS_HPlayerDataStorage);
1308impl_raw_handle!(Presence, sys::EOS_HPresence);
1309impl_raw_handle!(ProgressionSnapshot, sys::EOS_HProgressionSnapshot);
1310impl_raw_handle!(Reports, sys::EOS_HReports);
1311impl_raw_handle!(Rtc, sys::EOS_HRTC);
1312impl_raw_handle!(RtcAdmin, sys::EOS_HRTCAdmin);
1313impl_raw_handle!(Sanctions, sys::EOS_HSanctions);
1314impl_raw_handle!(Sessions, sys::EOS_HSessions);
1315impl_raw_handle!(Stats, sys::EOS_HStats);
1316impl_raw_handle!(TitleStorage, sys::EOS_HTitleStorage);
1317impl_raw_handle!(Ui, sys::EOS_HUI);
1318impl_raw_handle!(UserInfo, sys::EOS_HUserInfo);
1319
1320impl Lobby {
1321 pub fn raw_handle(&self) -> sys::EOS_HLobby {
1322 self.0.as_ptr() as sys::EOS_HLobby
1323 }
1324
1325 pub fn get_invite_count(&self, local_user: ProductUserId) -> u32 {
1326 let opts = sys::EOS_Lobby_GetInviteCountOptions {
1327 ApiVersion: sys::EOS_LOBBY_GETINVITECOUNT_API_LATEST as i32,
1328 LocalUserId: local_user.raw(),
1329 };
1330 unsafe { sys::EOS_Lobby_GetInviteCount(self.raw_handle(), &opts) }
1331 }
1332
1333 pub fn get_invite_id_by_index(&self, local_user: ProductUserId, index: u32) -> Result<String> {
1334 let opts = sys::EOS_Lobby_GetInviteIdByIndexOptions {
1335 ApiVersion: sys::EOS_LOBBY_GETINVITEIDBYINDEX_API_LATEST as i32,
1336 LocalUserId: local_user.raw(),
1337 Index: index,
1338 };
1339 let mut buf = vec![0i8; (sys::EOS_LOBBY_INVITEID_MAX_LENGTH + 1) as usize];
1340 let mut len = buf.len() as i32;
1341 let res =
1342 unsafe { sys::EOS_Lobby_GetInviteIdByIndex(self.raw_handle(), &opts, buf.as_mut_ptr(), &mut len) };
1343 ok(res)?;
1344 Ok(unsafe { CStr::from_ptr(buf.as_ptr()) }
1345 .to_string_lossy()
1346 .into_owned())
1347 }
1348
1349 pub fn create_lobby_search(&self, max_results: u32) -> Result<LobbySearch> {
1350 let opts = sys::EOS_Lobby_CreateLobbySearchOptions {
1351 ApiVersion: sys::EOS_LOBBY_CREATELOBBYSEARCH_API_LATEST as i32,
1352 MaxResults: max_results,
1353 };
1354 let mut out: sys::EOS_HLobbySearch = std::ptr::null_mut();
1355 let res = unsafe { sys::EOS_Lobby_CreateLobbySearch(self.raw_handle(), &opts, &mut out) };
1356 ok(res)?;
1357 unsafe { LobbySearch::from_raw(out) }
1358 }
1359
1360 pub fn copy_lobby_details_handle(
1361 &self,
1362 lobby_id: &str,
1363 local_user: ProductUserId,
1364 ) -> Result<LobbyDetails> {
1365 let lobby_id = CString::new(lobby_id)?;
1366 let opts = sys::EOS_Lobby_CopyLobbyDetailsHandleOptions {
1367 ApiVersion: sys::EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST as i32,
1368 LobbyId: lobby_id.as_ptr(),
1369 LocalUserId: local_user.raw(),
1370 };
1371 let mut out: sys::EOS_HLobbyDetails = std::ptr::null_mut();
1372 let res = unsafe { sys::EOS_Lobby_CopyLobbyDetailsHandle(self.raw_handle(), &opts, &mut out) };
1373 ok(res)?;
1374 unsafe { LobbyDetails::from_raw(out) }
1375 }
1376
1377 pub fn update_lobby_modification(
1378 &self,
1379 local_user: ProductUserId,
1380 lobby_id: &str,
1381 ) -> Result<LobbyModification> {
1382 let lobby_id = CString::new(lobby_id)?;
1383 let opts = sys::EOS_Lobby_UpdateLobbyModificationOptions {
1384 ApiVersion: sys::EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST as i32,
1385 LocalUserId: local_user.raw(),
1386 LobbyId: lobby_id.as_ptr(),
1387 };
1388 let mut out: sys::EOS_HLobbyModification = std::ptr::null_mut();
1389 let res = unsafe { sys::EOS_Lobby_UpdateLobbyModification(self.raw_handle(), &opts, &mut out) };
1390 ok(res)?;
1391 unsafe { LobbyModification::from_raw(out) }
1392 }
1393
1394 pub fn get_rtc_room_name(&self, lobby_id: &str, local_user: ProductUserId) -> Result<String> {
1395 let lobby_id = CString::new(lobby_id)?;
1396 let opts = sys::EOS_Lobby_GetRTCRoomNameOptions {
1397 ApiVersion: sys::EOS_LOBBY_GETRTCROOMNAME_API_LATEST as i32,
1398 LobbyId: lobby_id.as_ptr(),
1399 LocalUserId: local_user.raw(),
1400 };
1401 let mut buf = vec![0i8; 256];
1404 let mut len = buf.len() as u32;
1405 let res =
1406 unsafe { sys::EOS_Lobby_GetRTCRoomName(self.raw_handle(), &opts, buf.as_mut_ptr(), &mut len) };
1407 ok(res)?;
1408 Ok(unsafe { CStr::from_ptr(buf.as_ptr()) }
1409 .to_string_lossy()
1410 .into_owned())
1411 }
1412
1413 pub fn create_lobby(
1414 &self,
1415 local_user: ProductUserId,
1416 params: &CreateLobbyParams,
1417 cb: impl FnOnce(Result<sys::EOS_Lobby_CreateLobbyCallbackInfo>) + Send + 'static,
1418 ) -> Result<()> {
1419 #[repr(C)]
1420 struct Cb {
1421 f: Option<Box<dyn FnOnce(Result<sys::EOS_Lobby_CreateLobbyCallbackInfo>) + Send>>,
1422 }
1423 unsafe extern "C" fn trampoline(data: *const sys::EOS_Lobby_CreateLobbyCallbackInfo) {
1424 let client_data = (*data).ClientData as *mut Cb;
1425 let mut boxed = Box::from_raw(client_data);
1426 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1427 Ok(*data)
1428 } else {
1429 Err(Error::Eos((*data).ResultCode))
1430 };
1431 if let Some(f) = boxed.f.take() {
1432 f(res);
1433 }
1434 }
1435 let cb_box = CallbackOnce::new(Cb {
1436 f: Some(Box::new(cb)),
1437 });
1438
1439 let bucket_id = CString::new(params.bucket_id.clone())?;
1440 let options = sys::EOS_Lobby_CreateLobbyOptions {
1441 ApiVersion: sys::EOS_LOBBY_CREATELOBBY_API_LATEST as i32,
1442 LocalUserId: local_user.raw(),
1443 MaxLobbyMembers: params.max_lobby_members,
1444 PermissionLevel: params.permission_level,
1445 bPresenceEnabled: if params.presence_enabled { 1 } else { 0 },
1446 bAllowInvites: if params.allow_invites { 1 } else { 0 },
1447 BucketId: bucket_id.as_ptr(),
1448 bDisableHostMigration: if params.disable_host_migration { 1 } else { 0 },
1449 bEnableRTCRoom: if params.enable_rtc_room { 1 } else { 0 },
1450 LocalRTCOptions: std::ptr::null(),
1451 LobbyId: std::ptr::null(),
1452 bEnableJoinById: if params.enable_join_by_id { 1 } else { 0 },
1453 bRejoinAfterKickRequiresInvite: if params.rejoin_after_kick_requires_invite { 1 } else { 0 },
1454 AllowedPlatformIds: std::ptr::null(),
1455 AllowedPlatformIdsCount: 0,
1456 bCrossplayOptOut: 0,
1457 RTCRoomJoinActionType: sys::EOS_ELobbyRTCRoomJoinActionType_EOS_LRRJAT_AutomaticJoin,
1458 };
1459
1460 unsafe {
1461 sys::EOS_Lobby_CreateLobby(
1462 self.raw_handle(),
1463 &options,
1464 cb_box.ptr as *mut _,
1465 Some(trampoline),
1466 );
1467 }
1468 Ok(())
1469 }
1470
1471 pub fn join_lobby(
1472 &self,
1473 lobby_details: &LobbyDetails,
1474 local_user: ProductUserId,
1475 presence_enabled: bool,
1476 cb: impl FnOnce(Result<sys::EOS_Lobby_JoinLobbyCallbackInfo>) + Send + 'static,
1477 ) {
1478 #[repr(C)]
1479 struct Cb {
1480 f: Option<Box<dyn FnOnce(Result<sys::EOS_Lobby_JoinLobbyCallbackInfo>) + Send>>,
1481 }
1482 unsafe extern "C" fn trampoline(data: *const sys::EOS_Lobby_JoinLobbyCallbackInfo) {
1483 let client_data = (*data).ClientData as *mut Cb;
1484 let mut boxed = Box::from_raw(client_data);
1485 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1486 Ok(*data)
1487 } else {
1488 Err(Error::Eos((*data).ResultCode))
1489 };
1490 if let Some(f) = boxed.f.take() {
1491 f(res);
1492 }
1493 }
1494 let cb_box = CallbackOnce::new(Cb {
1495 f: Some(Box::new(cb)),
1496 });
1497 let options = sys::EOS_Lobby_JoinLobbyOptions {
1498 ApiVersion: sys::EOS_LOBBY_JOINLOBBY_API_LATEST as i32,
1499 LobbyDetailsHandle: lobby_details.raw_handle(),
1500 LocalUserId: local_user.raw(),
1501 bPresenceEnabled: if presence_enabled { 1 } else { 0 },
1502 LocalRTCOptions: std::ptr::null(),
1503 bCrossplayOptOut: 0,
1504 RTCRoomJoinActionType: sys::EOS_ELobbyRTCRoomJoinActionType_EOS_LRRJAT_AutomaticJoin,
1505 };
1506 unsafe {
1507 sys::EOS_Lobby_JoinLobby(
1508 self.raw_handle(),
1509 &options,
1510 cb_box.ptr as *mut _,
1511 Some(trampoline),
1512 );
1513 }
1514 }
1515
1516 pub fn leave_lobby(
1517 &self,
1518 local_user: ProductUserId,
1519 lobby_id: &str,
1520 cb: impl FnOnce(Result<sys::EOS_Lobby_LeaveLobbyCallbackInfo>) + Send + 'static,
1521 ) -> Result<()> {
1522 #[repr(C)]
1523 struct Cb {
1524 f: Option<Box<dyn FnOnce(Result<sys::EOS_Lobby_LeaveLobbyCallbackInfo>) + Send>>,
1525 }
1526 unsafe extern "C" fn trampoline(data: *const sys::EOS_Lobby_LeaveLobbyCallbackInfo) {
1527 let client_data = (*data).ClientData as *mut Cb;
1528 let mut boxed = Box::from_raw(client_data);
1529 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1530 Ok(*data)
1531 } else {
1532 Err(Error::Eos((*data).ResultCode))
1533 };
1534 if let Some(f) = boxed.f.take() {
1535 f(res);
1536 }
1537 }
1538 let cb_box = CallbackOnce::new(Cb {
1539 f: Some(Box::new(cb)),
1540 });
1541 let lobby_id = CString::new(lobby_id)?;
1542 let options = sys::EOS_Lobby_LeaveLobbyOptions {
1543 ApiVersion: sys::EOS_LOBBY_LEAVELOBBY_API_LATEST as i32,
1544 LobbyId: lobby_id.as_ptr(),
1545 LocalUserId: local_user.raw(),
1546 };
1547 unsafe {
1548 sys::EOS_Lobby_LeaveLobby(
1549 self.raw_handle(),
1550 &options,
1551 cb_box.ptr as *mut _,
1552 Some(trampoline),
1553 );
1554 }
1555 Ok(())
1556 }
1557
1558 pub fn destroy_lobby(
1559 &self,
1560 local_user: ProductUserId,
1561 lobby_id: &str,
1562 cb: impl FnOnce(Result<sys::EOS_Lobby_DestroyLobbyCallbackInfo>) + Send + 'static,
1563 ) -> Result<()> {
1564 #[repr(C)]
1565 struct Cb {
1566 f: Option<Box<dyn FnOnce(Result<sys::EOS_Lobby_DestroyLobbyCallbackInfo>) + Send>>,
1567 }
1568 unsafe extern "C" fn trampoline(data: *const sys::EOS_Lobby_DestroyLobbyCallbackInfo) {
1569 let client_data = (*data).ClientData as *mut Cb;
1570 let mut boxed = Box::from_raw(client_data);
1571 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1572 Ok(*data)
1573 } else {
1574 Err(Error::Eos((*data).ResultCode))
1575 };
1576 if let Some(f) = boxed.f.take() {
1577 f(res);
1578 }
1579 }
1580 let cb_box = CallbackOnce::new(Cb {
1581 f: Some(Box::new(cb)),
1582 });
1583 let lobby_id = CString::new(lobby_id)?;
1584 let options = sys::EOS_Lobby_DestroyLobbyOptions {
1585 ApiVersion: sys::EOS_LOBBY_DESTROYLOBBY_API_LATEST as i32,
1586 LocalUserId: local_user.raw(),
1587 LobbyId: lobby_id.as_ptr(),
1588 };
1589 unsafe {
1590 sys::EOS_Lobby_DestroyLobby(
1591 self.raw_handle(),
1592 &options,
1593 cb_box.ptr as *mut _,
1594 Some(trampoline),
1595 );
1596 }
1597 Ok(())
1598 }
1599}
1600
1601impl P2P {
1602 pub fn raw_handle(&self) -> sys::EOS_HP2P {
1603 self.0.as_ptr() as sys::EOS_HP2P
1604 }
1605
1606 pub fn query_nat_type(
1607 &self,
1608 cb: impl FnOnce(Result<NatType>) + Send + 'static,
1609 ) {
1610 #[repr(C)]
1611 struct Cb {
1612 f: Option<Box<dyn FnOnce(Result<NatType>) + Send>>,
1613 }
1614 unsafe extern "C" fn trampoline(data: *const sys::EOS_P2P_OnQueryNATTypeCompleteInfo) {
1615 let client_data = (*data).ClientData as *mut Cb;
1616 let mut boxed = Box::from_raw(client_data);
1617 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
1618 Ok(NatType::from_raw((*data).NATType))
1619 } else {
1620 Err(Error::Eos((*data).ResultCode))
1621 };
1622 if let Some(f) = boxed.f.take() {
1623 f(res);
1624 }
1625 }
1626
1627 let cb_box = CallbackOnce::new(Cb {
1628 f: Some(Box::new(cb)),
1629 });
1630 let opts = sys::EOS_P2P_QueryNATTypeOptions {
1631 ApiVersion: sys::EOS_P2P_QUERYNATTYPE_API_LATEST as i32,
1632 };
1633 unsafe {
1634 sys::EOS_P2P_QueryNATType(self.raw_handle(), &opts, cb_box.ptr as *mut _, Some(trampoline));
1635 }
1636 }
1637
1638 pub fn get_nat_type(&self) -> Result<NatType> {
1639 let opts = sys::EOS_P2P_GetNATTypeOptions {
1640 ApiVersion: sys::EOS_P2P_GETNATTYPE_API_LATEST as i32,
1641 };
1642 let mut out = sys::EOS_ENATType_EOS_NAT_Unknown;
1643 let res = unsafe { sys::EOS_P2P_GetNATType(self.raw_handle(), &opts, &mut out) };
1644 ok(res)?;
1645 Ok(NatType::from_raw(out))
1646 }
1647
1648 pub fn set_relay_control(&self, relay: RelayControl) -> Result<()> {
1649 let opts = sys::EOS_P2P_SetRelayControlOptions {
1650 ApiVersion: sys::EOS_P2P_SETRELAYCONTROL_API_LATEST as i32,
1651 RelayControl: relay.to_raw(),
1652 };
1653 let res = unsafe { sys::EOS_P2P_SetRelayControl(self.raw_handle(), &opts) };
1654 ok(res)
1655 }
1656
1657 pub fn get_relay_control(&self) -> Result<RelayControl> {
1658 let opts = sys::EOS_P2P_GetRelayControlOptions {
1659 ApiVersion: sys::EOS_P2P_GETRELAYCONTROL_API_LATEST as i32,
1660 };
1661 let mut out = sys::EOS_ERelayControl_EOS_RC_AllowRelays;
1662 let res = unsafe { sys::EOS_P2P_GetRelayControl(self.raw_handle(), &opts, &mut out) };
1663 ok(res)?;
1664 Ok(RelayControl::from_raw(out))
1665 }
1666
1667 pub fn set_port_range(&self, port: u16, max_additional_ports: u16) -> Result<()> {
1668 let opts = sys::EOS_P2P_SetPortRangeOptions {
1669 ApiVersion: sys::EOS_P2P_SETPORTRANGE_API_LATEST as i32,
1670 Port: port,
1671 MaxAdditionalPortsToTry: max_additional_ports,
1672 };
1673 let res = unsafe { sys::EOS_P2P_SetPortRange(self.raw_handle(), &opts) };
1674 ok(res)
1675 }
1676
1677 pub fn get_port_range(&self) -> Result<(u16, u16)> {
1678 let opts = sys::EOS_P2P_GetPortRangeOptions {
1679 ApiVersion: sys::EOS_P2P_GETPORTRANGE_API_LATEST as i32,
1680 };
1681 let mut port = 0u16;
1682 let mut extra = 0u16;
1683 let res = unsafe { sys::EOS_P2P_GetPortRange(self.raw_handle(), &opts, &mut port, &mut extra) };
1684 ok(res)?;
1685 Ok((port, extra))
1686 }
1687
1688 pub fn set_packet_queue_size(&self, incoming_max: u64, outgoing_max: u64) -> Result<()> {
1689 let opts = sys::EOS_P2P_SetPacketQueueSizeOptions {
1690 ApiVersion: sys::EOS_P2P_SETPACKETQUEUESIZE_API_LATEST as i32,
1691 IncomingPacketQueueMaxSizeBytes: incoming_max,
1692 OutgoingPacketQueueMaxSizeBytes: outgoing_max,
1693 };
1694 let res = unsafe { sys::EOS_P2P_SetPacketQueueSize(self.raw_handle(), &opts) };
1695 ok(res)
1696 }
1697
1698 pub fn get_packet_queue_info(&self) -> Result<PacketQueueInfo> {
1699 let opts = sys::EOS_P2P_GetPacketQueueInfoOptions {
1700 ApiVersion: sys::EOS_P2P_GETPACKETQUEUEINFO_API_LATEST as i32,
1701 };
1702 let mut out = sys::EOS_P2P_PacketQueueInfo::default();
1703 let res = unsafe { sys::EOS_P2P_GetPacketQueueInfo(self.raw_handle(), &opts, &mut out) };
1704 ok(res)?;
1705 Ok(PacketQueueInfo {
1706 incoming_max_size_bytes: out.IncomingPacketQueueMaxSizeBytes,
1707 incoming_current_size_bytes: out.IncomingPacketQueueCurrentSizeBytes,
1708 incoming_current_packet_count: out.IncomingPacketQueueCurrentPacketCount,
1709 outgoing_max_size_bytes: out.OutgoingPacketQueueMaxSizeBytes,
1710 outgoing_current_size_bytes: out.OutgoingPacketQueueCurrentSizeBytes,
1711 outgoing_current_packet_count: out.OutgoingPacketQueueCurrentPacketCount,
1712 })
1713 }
1714
1715 pub fn send_packet(
1716 &self,
1717 local_user: ProductUserId,
1718 remote_user: ProductUserId,
1719 socket_name: &str,
1720 channel: u8,
1721 data: &[u8],
1722 reliability: PacketReliability,
1723 allow_delayed_delivery: bool,
1724 disable_auto_accept_connection: bool,
1725 ) -> Result<()> {
1726 let socket = make_socket_id(socket_name)?;
1727 let opts = sys::EOS_P2P_SendPacketOptions {
1728 ApiVersion: sys::EOS_P2P_SENDPACKET_API_LATEST as i32,
1729 LocalUserId: local_user.raw(),
1730 RemoteUserId: remote_user.raw(),
1731 SocketId: &socket,
1732 Channel: channel,
1733 DataLengthBytes: data.len() as u32,
1734 Data: data.as_ptr() as *const _,
1735 bAllowDelayedDelivery: if allow_delayed_delivery { 1 } else { 0 },
1736 Reliability: reliability.to_raw(),
1737 bDisableAutoAcceptConnection: if disable_auto_accept_connection { 1 } else { 0 },
1738 };
1739 let res = unsafe { sys::EOS_P2P_SendPacket(self.raw_handle(), &opts) };
1740 ok(res)
1741 }
1742
1743 pub fn get_next_received_packet_size(
1744 &self,
1745 local_user: ProductUserId,
1746 requested_channel: Option<u8>,
1747 ) -> Result<u32> {
1748 let requested = requested_channel.unwrap_or_default();
1749 let requested_ptr = if requested_channel.is_some() {
1750 &requested as *const u8
1751 } else {
1752 std::ptr::null()
1753 };
1754 let opts = sys::EOS_P2P_GetNextReceivedPacketSizeOptions {
1755 ApiVersion: sys::EOS_P2P_GETNEXTRECEIVEDPACKETSIZE_API_LATEST as i32,
1756 LocalUserId: local_user.raw(),
1757 RequestedChannel: requested_ptr,
1758 };
1759 let mut size = 0u32;
1760 let res = unsafe { sys::EOS_P2P_GetNextReceivedPacketSize(self.raw_handle(), &opts, &mut size) };
1761 ok(res)?;
1762 Ok(size)
1763 }
1764
1765 pub fn receive_packet(
1766 &self,
1767 local_user: ProductUserId,
1768 max_data_size_bytes: u32,
1769 requested_channel: Option<u8>,
1770 ) -> Result<ReceivedPacket> {
1771 let requested = requested_channel.unwrap_or_default();
1772 let requested_ptr = if requested_channel.is_some() {
1773 &requested as *const u8
1774 } else {
1775 std::ptr::null()
1776 };
1777 let opts = sys::EOS_P2P_ReceivePacketOptions {
1778 ApiVersion: sys::EOS_P2P_RECEIVEPACKET_API_LATEST as i32,
1779 LocalUserId: local_user.raw(),
1780 MaxDataSizeBytes: max_data_size_bytes,
1781 RequestedChannel: requested_ptr,
1782 };
1783 let mut peer = std::ptr::null_mut();
1784 let mut socket = sys::EOS_P2P_SocketId::default();
1785 let mut channel = 0u8;
1786 let mut data = vec![0u8; max_data_size_bytes as usize];
1787 let mut bytes_written = 0u32;
1788 let res = unsafe {
1789 sys::EOS_P2P_ReceivePacket(
1790 self.raw_handle(),
1791 &opts,
1792 &mut peer,
1793 &mut socket,
1794 &mut channel,
1795 data.as_mut_ptr() as *mut _,
1796 &mut bytes_written,
1797 )
1798 };
1799 ok(res)?;
1800 data.truncate(bytes_written as usize);
1801 Ok(ReceivedPacket {
1802 peer_id: ProductUserId(peer),
1803 socket_name: socket_name_from_raw(&socket),
1804 channel,
1805 data,
1806 })
1807 }
1808
1809 pub fn accept_connection(
1810 &self,
1811 local_user: ProductUserId,
1812 remote_user: ProductUserId,
1813 socket_name: &str,
1814 ) -> Result<()> {
1815 let socket = make_socket_id(socket_name)?;
1816 let opts = sys::EOS_P2P_AcceptConnectionOptions {
1817 ApiVersion: sys::EOS_P2P_ACCEPTCONNECTION_API_LATEST as i32,
1818 LocalUserId: local_user.raw(),
1819 RemoteUserId: remote_user.raw(),
1820 SocketId: &socket,
1821 };
1822 ok(unsafe { sys::EOS_P2P_AcceptConnection(self.raw_handle(), &opts) })
1823 }
1824
1825 pub fn close_connection(
1826 &self,
1827 local_user: ProductUserId,
1828 remote_user: ProductUserId,
1829 socket_name: Option<&str>,
1830 ) -> Result<()> {
1831 let socket = match socket_name {
1832 Some(n) => Some(make_socket_id(n)?),
1833 None => None,
1834 };
1835 let opts = sys::EOS_P2P_CloseConnectionOptions {
1836 ApiVersion: sys::EOS_P2P_CLOSECONNECTION_API_LATEST as i32,
1837 LocalUserId: local_user.raw(),
1838 RemoteUserId: remote_user.raw(),
1839 SocketId: socket
1840 .as_ref()
1841 .map(|s| s as *const _)
1842 .unwrap_or(std::ptr::null()),
1843 };
1844 ok(unsafe { sys::EOS_P2P_CloseConnection(self.raw_handle(), &opts) })
1845 }
1846
1847 pub fn close_connections(&self, local_user: ProductUserId, socket_name: &str) -> Result<()> {
1848 let socket = make_socket_id(socket_name)?;
1849 let opts = sys::EOS_P2P_CloseConnectionsOptions {
1850 ApiVersion: sys::EOS_P2P_CLOSECONNECTIONS_API_LATEST as i32,
1851 LocalUserId: local_user.raw(),
1852 SocketId: &socket,
1853 };
1854 ok(unsafe { sys::EOS_P2P_CloseConnections(self.raw_handle(), &opts) })
1855 }
1856
1857 pub fn clear_packet_queue(
1858 &self,
1859 local_user: ProductUserId,
1860 remote_user: ProductUserId,
1861 socket_name: &str,
1862 ) -> Result<()> {
1863 let socket = make_socket_id(socket_name)?;
1864 let opts = sys::EOS_P2P_ClearPacketQueueOptions {
1865 ApiVersion: sys::EOS_P2P_CLEARPACKETQUEUE_API_LATEST as i32,
1866 LocalUserId: local_user.raw(),
1867 RemoteUserId: remote_user.raw(),
1868 SocketId: &socket,
1869 };
1870 ok(unsafe { sys::EOS_P2P_ClearPacketQueue(self.raw_handle(), &opts) })
1871 }
1872}
1873
1874macro_rules! owned_ptr_release {
1877 ($name:ident, $inner:ty, $release:path) => {
1878 pub struct $name(NonNull<$inner>);
1879
1880 impl $name {
1881 pub unsafe fn from_raw(ptr: *mut $inner) -> Result<Self> {
1882 Ok(Self(NonNull::new(ptr).ok_or(Error::Null)?))
1883 }
1884
1885 pub fn as_ptr(&self) -> *mut $inner {
1886 self.0.as_ptr()
1887 }
1888
1889 pub fn into_raw(self) -> *mut $inner {
1890 let p = self.0.as_ptr();
1891 std::mem::forget(self);
1892 p
1893 }
1894 }
1895
1896 impl Drop for $name {
1897 fn drop(&mut self) {
1898 unsafe { $release(self.0.as_ptr()) }
1899 }
1900 }
1901 };
1902}
1903
1904macro_rules! owned_handle_release {
1905 ($name:ident, $handle:ty, $release:path) => {
1906 pub struct $name($handle);
1907
1908 impl $name {
1909 pub unsafe fn from_raw(h: $handle) -> Result<Self> {
1910 if (h as usize) == 0 {
1911 return Err(Error::Null);
1912 }
1913 Ok(Self(h))
1914 }
1915
1916 pub fn raw_handle(&self) -> $handle {
1917 self.0
1918 }
1919
1920 pub fn into_raw(self) -> $handle {
1921 let h = self.0;
1922 std::mem::forget(self);
1923 h
1924 }
1925 }
1926
1927 impl Drop for $name {
1928 fn drop(&mut self) {
1929 unsafe { $release(self.0) }
1930 }
1931 }
1932 };
1933}
1934
1935owned_ptr_release!(AuthToken, sys::EOS_Auth_Token, sys::EOS_Auth_Token_Release);
1936owned_ptr_release!(AuthIdToken, sys::EOS_Auth_IdToken, sys::EOS_Auth_IdToken_Release);
1937owned_ptr_release!(
1938 ConnectExternalAccountInfo,
1939 sys::EOS_Connect_ExternalAccountInfo,
1940 sys::EOS_Connect_ExternalAccountInfo_Release
1941);
1942owned_ptr_release!(ConnectIdToken, sys::EOS_Connect_IdToken, sys::EOS_Connect_IdToken_Release);
1943owned_ptr_release!(
1944 EcomEntitlement,
1945 sys::EOS_Ecom_Entitlement,
1946 sys::EOS_Ecom_Entitlement_Release
1947);
1948owned_ptr_release!(
1949 EcomCatalogItem,
1950 sys::EOS_Ecom_CatalogItem,
1951 sys::EOS_Ecom_CatalogItem_Release
1952);
1953owned_ptr_release!(
1954 EcomCatalogOffer,
1955 sys::EOS_Ecom_CatalogOffer,
1956 sys::EOS_Ecom_CatalogOffer_Release
1957);
1958owned_ptr_release!(
1959 EcomKeyImageInfo,
1960 sys::EOS_Ecom_KeyImageInfo,
1961 sys::EOS_Ecom_KeyImageInfo_Release
1962);
1963owned_ptr_release!(
1964 EcomCatalogRelease,
1965 sys::EOS_Ecom_CatalogRelease,
1966 sys::EOS_Ecom_CatalogRelease_Release
1967);
1968owned_handle_release!(
1969 EcomTransaction,
1970 sys::EOS_Ecom_HTransaction,
1971 sys::EOS_Ecom_Transaction_Release
1972);
1973owned_ptr_release!(PresenceInfo, sys::EOS_Presence_Info, sys::EOS_Presence_Info_Release);
1974owned_handle_release!(
1975 PresenceModification,
1976 sys::EOS_HPresenceModification,
1977 sys::EOS_PresenceModification_Release
1978);
1979owned_handle_release!(
1980 SessionModification,
1981 sys::EOS_HSessionModification,
1982 sys::EOS_SessionModification_Release
1983);
1984owned_handle_release!(
1985 ActiveSession,
1986 sys::EOS_HActiveSession,
1987 sys::EOS_ActiveSession_Release
1988);
1989owned_handle_release!(
1990 SessionDetails,
1991 sys::EOS_HSessionDetails,
1992 sys::EOS_SessionDetails_Release
1993);
1994owned_handle_release!(
1995 SessionSearch,
1996 sys::EOS_HSessionSearch,
1997 sys::EOS_SessionSearch_Release
1998);
1999owned_ptr_release!(
2000 SessionDetailsAttribute,
2001 sys::EOS_SessionDetails_Attribute,
2002 sys::EOS_SessionDetails_Attribute_Release
2003);
2004owned_ptr_release!(
2005 SessionDetailsInfo,
2006 sys::EOS_SessionDetails_Info,
2007 sys::EOS_SessionDetails_Info_Release
2008);
2009owned_ptr_release!(
2010 ActiveSessionInfo,
2011 sys::EOS_ActiveSession_Info,
2012 sys::EOS_ActiveSession_Info_Release
2013);
2014owned_handle_release!(
2015 LobbyModification,
2016 sys::EOS_HLobbyModification,
2017 sys::EOS_LobbyModification_Release
2018);
2019owned_handle_release!(LobbyDetails, sys::EOS_HLobbyDetails, sys::EOS_LobbyDetails_Release);
2020owned_handle_release!(LobbySearch, sys::EOS_HLobbySearch, sys::EOS_LobbySearch_Release);
2021owned_ptr_release!(
2022 LobbyDetailsInfo,
2023 sys::EOS_LobbyDetails_Info,
2024 sys::EOS_LobbyDetails_Info_Release
2025);
2026owned_ptr_release!(
2027 LobbyAttribute,
2028 sys::EOS_Lobby_Attribute,
2029 sys::EOS_Lobby_Attribute_Release
2030);
2031owned_ptr_release!(
2032 LobbyMemberInfo,
2033 sys::EOS_LobbyDetails_MemberInfo,
2034 sys::EOS_LobbyDetails_MemberInfo_Release
2035);
2036owned_ptr_release!(UserInfoData, sys::EOS_UserInfo, sys::EOS_UserInfo_Release);
2037owned_ptr_release!(
2038 ExternalUserInfo,
2039 sys::EOS_UserInfo_ExternalUserInfo,
2040 sys::EOS_UserInfo_ExternalUserInfo_Release
2041);
2042owned_ptr_release!(
2043 BestDisplayName,
2044 sys::EOS_UserInfo_BestDisplayName,
2045 sys::EOS_UserInfo_BestDisplayName_Release
2046);
2047owned_ptr_release!(
2048 PlayerDataStorageFileMetadata,
2049 sys::EOS_PlayerDataStorage_FileMetadata,
2050 sys::EOS_PlayerDataStorage_FileMetadata_Release
2051);
2052owned_handle_release!(
2053 PlayerDataStorageFileTransferRequest,
2054 sys::EOS_HPlayerDataStorageFileTransferRequest,
2055 sys::EOS_PlayerDataStorageFileTransferRequest_Release
2056);
2057owned_ptr_release!(
2058 TitleStorageFileMetadata,
2059 sys::EOS_TitleStorage_FileMetadata,
2060 sys::EOS_TitleStorage_FileMetadata_Release
2061);
2062owned_handle_release!(
2063 TitleStorageFileTransferRequest,
2064 sys::EOS_HTitleStorageFileTransferRequest,
2065 sys::EOS_TitleStorageFileTransferRequest_Release
2066);
2067owned_ptr_release!(
2068 AchievementsDefinitionV2,
2069 sys::EOS_Achievements_DefinitionV2,
2070 sys::EOS_Achievements_DefinitionV2_Release
2071);
2072owned_ptr_release!(
2073 AchievementsPlayerAchievement,
2074 sys::EOS_Achievements_PlayerAchievement,
2075 sys::EOS_Achievements_PlayerAchievement_Release
2076);
2077owned_ptr_release!(
2078 AchievementsDefinition,
2079 sys::EOS_Achievements_Definition,
2080 sys::EOS_Achievements_Definition_Release
2081);
2082owned_ptr_release!(
2083 AchievementsUnlockedAchievement,
2084 sys::EOS_Achievements_UnlockedAchievement,
2085 sys::EOS_Achievements_UnlockedAchievement_Release
2086);
2087owned_ptr_release!(StatsStat, sys::EOS_Stats_Stat, sys::EOS_Stats_Stat_Release);
2088owned_ptr_release!(
2089 LeaderboardsDefinition,
2090 sys::EOS_Leaderboards_Definition,
2091 sys::EOS_Leaderboards_Definition_Release
2092);
2093owned_ptr_release!(
2094 LeaderboardsUserScore,
2095 sys::EOS_Leaderboards_LeaderboardUserScore,
2096 sys::EOS_Leaderboards_LeaderboardUserScore_Release
2097);
2098owned_ptr_release!(
2099 LeaderboardsRecord,
2100 sys::EOS_Leaderboards_LeaderboardRecord,
2101 sys::EOS_Leaderboards_LeaderboardRecord_Release
2102);
2103owned_ptr_release!(
2104 LeaderboardsLeaderboardDefinition,
2105 sys::EOS_Leaderboards_Definition,
2106 sys::EOS_Leaderboards_LeaderboardDefinition_Release
2107);
2108owned_ptr_release!(ModsModInfo, sys::EOS_Mods_ModInfo, sys::EOS_Mods_ModInfo_Release);
2109owned_ptr_release!(
2110 SanctionsPlayerSanction,
2111 sys::EOS_Sanctions_PlayerSanction,
2112 sys::EOS_Sanctions_PlayerSanction_Release
2113);
2114owned_ptr_release!(
2115 KwsPermissionStatus,
2116 sys::EOS_KWS_PermissionStatus,
2117 sys::EOS_KWS_PermissionStatus_Release
2118);
2119owned_ptr_release!(
2120 RtcAdminUserToken,
2121 sys::EOS_RTCAdmin_UserToken,
2122 sys::EOS_RTCAdmin_UserToken_Release
2123);
2124
2125impl LobbySearch {
2126 pub fn set_lobby_id(&self, lobby_id: &str) -> Result<()> {
2127 let lobby_id = CString::new(lobby_id)?;
2128 let opts = sys::EOS_LobbySearch_SetLobbyIdOptions {
2129 ApiVersion: sys::EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST as i32,
2130 LobbyId: lobby_id.as_ptr(),
2131 };
2132 ok(unsafe { sys::EOS_LobbySearch_SetLobbyId(self.raw_handle(), &opts) })
2133 }
2134
2135 pub fn set_target_user_id(&self, target_user_id: ProductUserId) -> Result<()> {
2136 let opts = sys::EOS_LobbySearch_SetTargetUserIdOptions {
2137 ApiVersion: sys::EOS_LOBBYSEARCH_SETTARGETUSERID_API_LATEST as i32,
2138 TargetUserId: target_user_id.raw(),
2139 };
2140 ok(unsafe { sys::EOS_LobbySearch_SetTargetUserId(self.raw_handle(), &opts) })
2141 }
2142
2143 pub fn set_max_results(&self, max_results: u32) -> Result<()> {
2144 let opts = sys::EOS_LobbySearch_SetMaxResultsOptions {
2145 ApiVersion: sys::EOS_LOBBYSEARCH_SETMAXRESULTS_API_LATEST as i32,
2146 MaxResults: max_results,
2147 };
2148 ok(unsafe { sys::EOS_LobbySearch_SetMaxResults(self.raw_handle(), &opts) })
2149 }
2150
2151 pub fn set_parameter(
2152 &self,
2153 key: &str,
2154 value: &LobbySearchValue,
2155 comparison_op: sys::EOS_EComparisonOp,
2156 ) -> Result<()> {
2157 let key = CString::new(key)?;
2158 let string_storage;
2159 let (value_union, value_type) = match value {
2160 LobbySearchValue::Bool(v) => (
2161 sys::_tagEOS_Lobby_AttributeData__bindgen_ty_1 {
2162 AsBool: if *v { 1 } else { 0 },
2163 },
2164 sys::EOS_EAttributeType_EOS_AT_BOOLEAN,
2165 ),
2166 LobbySearchValue::Int64(v) => (
2167 sys::_tagEOS_Lobby_AttributeData__bindgen_ty_1 { AsInt64: *v },
2168 sys::EOS_EAttributeType_EOS_AT_INT64,
2169 ),
2170 LobbySearchValue::Double(v) => (
2171 sys::_tagEOS_Lobby_AttributeData__bindgen_ty_1 { AsDouble: *v },
2172 sys::EOS_EAttributeType_EOS_AT_DOUBLE,
2173 ),
2174 LobbySearchValue::String(v) => {
2175 string_storage = CString::new(v.as_str())?;
2176 (
2177 sys::_tagEOS_Lobby_AttributeData__bindgen_ty_1 {
2178 AsUtf8: string_storage.as_ptr(),
2179 },
2180 sys::EOS_EAttributeType_EOS_AT_STRING,
2181 )
2182 }
2183 };
2184
2185 let attr = sys::EOS_Lobby_AttributeData {
2186 ApiVersion: sys::EOS_LOBBY_ATTRIBUTEDATA_API_LATEST as i32,
2187 Key: key.as_ptr(),
2188 Value: value_union,
2189 ValueType: value_type,
2190 };
2191
2192 let opts = sys::EOS_LobbySearch_SetParameterOptions {
2193 ApiVersion: sys::EOS_LOBBYSEARCH_SETPARAMETER_API_LATEST as i32,
2194 Parameter: &attr,
2195 ComparisonOp: comparison_op,
2196 };
2197 ok(unsafe { sys::EOS_LobbySearch_SetParameter(self.raw_handle(), &opts) })
2198 }
2199
2200 pub fn remove_parameter(&self, key: &str, comparison_op: sys::EOS_EComparisonOp) -> Result<()> {
2201 let key = CString::new(key)?;
2202 let opts = sys::EOS_LobbySearch_RemoveParameterOptions {
2203 ApiVersion: sys::EOS_LOBBYSEARCH_REMOVEPARAMETER_API_LATEST as i32,
2204 Key: key.as_ptr(),
2205 ComparisonOp: comparison_op,
2206 };
2207 ok(unsafe { sys::EOS_LobbySearch_RemoveParameter(self.raw_handle(), &opts) })
2208 }
2209
2210 pub fn find(
2211 &self,
2212 local_user: ProductUserId,
2213 cb: impl FnOnce(Result<sys::EOS_LobbySearch_FindCallbackInfo>) + Send + 'static,
2214 ) {
2215 #[repr(C)]
2216 struct Cb {
2217 f: Option<Box<dyn FnOnce(Result<sys::EOS_LobbySearch_FindCallbackInfo>) + Send>>,
2218 }
2219 unsafe extern "C" fn trampoline(data: *const sys::EOS_LobbySearch_FindCallbackInfo) {
2220 let client_data = (*data).ClientData as *mut Cb;
2221 let mut boxed = Box::from_raw(client_data);
2222 let res = if (*data).ResultCode == sys::EOS_EResult_EOS_Success {
2223 Ok(*data)
2224 } else {
2225 Err(Error::Eos((*data).ResultCode))
2226 };
2227 if let Some(f) = boxed.f.take() {
2228 f(res);
2229 }
2230 }
2231
2232 let cb_box = CallbackOnce::new(Cb {
2233 f: Some(Box::new(cb)),
2234 });
2235 let opts = sys::EOS_LobbySearch_FindOptions {
2236 ApiVersion: sys::EOS_LOBBYSEARCH_FIND_API_LATEST as i32,
2237 LocalUserId: local_user.raw(),
2238 };
2239 unsafe {
2240 sys::EOS_LobbySearch_Find(self.raw_handle(), &opts, cb_box.ptr as *mut _, Some(trampoline));
2241 }
2242 }
2243
2244 pub fn get_search_result_count(&self) -> u32 {
2245 let opts = sys::EOS_LobbySearch_GetSearchResultCountOptions {
2246 ApiVersion: sys::EOS_LOBBYSEARCH_GETSEARCHRESULTCOUNT_API_LATEST as i32,
2247 };
2248 unsafe { sys::EOS_LobbySearch_GetSearchResultCount(self.raw_handle(), &opts) }
2249 }
2250
2251 pub fn copy_search_result_by_index(&self, lobby_index: u32) -> Result<LobbyDetails> {
2252 let opts = sys::EOS_LobbySearch_CopySearchResultByIndexOptions {
2253 ApiVersion: sys::EOS_LOBBYSEARCH_COPYSEARCHRESULTBYINDEX_API_LATEST as i32,
2254 LobbyIndex: lobby_index,
2255 };
2256 let mut out: sys::EOS_HLobbyDetails = std::ptr::null_mut();
2257 let res = unsafe { sys::EOS_LobbySearch_CopySearchResultByIndex(self.raw_handle(), &opts, &mut out) };
2258 ok(res)?;
2259 unsafe { LobbyDetails::from_raw(out) }
2260 }
2261}
2262
2263impl AuthToken {
2264 pub fn account_id(&self) -> EpicAccountId {
2265 let token = unsafe { &*self.as_ptr() };
2267 EpicAccountId(token.AccountId)
2268 }
2269
2270 pub fn access_token(&self) -> Option<&str> {
2271 let token = unsafe { &*self.as_ptr() };
2272 if token.AccessToken.is_null() {
2273 return None;
2274 }
2275 Some(unsafe { CStr::from_ptr(token.AccessToken) }.to_str().ok()?)
2276 }
2277}
2278
2279impl AuthIdToken {
2280 pub fn account_id(&self) -> EpicAccountId {
2281 let token = unsafe { &*self.as_ptr() };
2282 EpicAccountId(token.AccountId)
2283 }
2284
2285 pub fn json_web_token(&self) -> Option<&str> {
2286 let token = unsafe { &*self.as_ptr() };
2287 if token.JsonWebToken.is_null() {
2288 return None;
2289 }
2290 Some(unsafe { CStr::from_ptr(token.JsonWebToken) }.to_str().ok()?)
2291 }
2292}
2293
2294impl ConnectIdToken {
2295 pub fn product_user_id(&self) -> ProductUserId {
2296 let token = unsafe { &*self.as_ptr() };
2297 ProductUserId(token.ProductUserId)
2298 }
2299
2300 pub fn json_web_token(&self) -> Option<&str> {
2301 let token = unsafe { &*self.as_ptr() };
2302 if token.JsonWebToken.is_null() {
2303 return None;
2304 }
2305 Some(unsafe { CStr::from_ptr(token.JsonWebToken) }.to_str().ok()?)
2306 }
2307}
2308
2309