use crate::{NotThreadSafe, Server};
use sdk_raw::steam as raw;
use sdk_raw::util::{self, PinnedModule, rtti};
use std::ffi::{CStr, OsStr};
use std::marker::PhantomData;
use std::path::PathBuf;
use std::ptr::NonNull;
#[doc(alias("ISteamGameCoordinator"))]
#[derive(Debug, Clone, Copy)]
pub struct GameCoordinator<'s> {
ptr: NonNull<raw::ISteamGameCoordinator>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> GameCoordinator<'s> {
pub fn as_ptr(self) -> *mut raw::ISteamGameCoordinator {
self.ptr.as_ptr()
}
pub fn class_name(self) -> Option<&'s CStr> {
unsafe { rtti::dynamic_type(self.ptr.as_ptr().cast()) }.map(|(_, name)| name)
}
pub fn pin_steam_client(self) -> Result<PinnedModule, SteamClientError> {
let vtable = unsafe { self.ptr.as_ptr().cast::<usize>().read() };
let module = unsafe { util::pin_module(vtable) }?;
if !is_steam_client(module.path().file_name()) {
return Err(SteamClientError::NotSteamClient(module.path().to_owned()));
}
Ok(module)
}
}
#[doc(alias("EGCResults"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum GcResult {
#[doc(alias("k_EGCResultOK"))]
Ok = raw::GC_RESULT_OK,
#[doc(alias("k_EGCResultNoMessage"))]
NoMessage = raw::GC_RESULT_NO_MESSAGE,
#[doc(alias("k_EGCResultBufferTooSmall"))]
BufferTooSmall = raw::GC_RESULT_BUFFER_TOO_SMALL,
#[doc(alias("k_EGCResultNotLoggedOn"))]
NotLoggedOn = raw::GC_RESULT_NOT_LOGGED_ON,
#[doc(alias("k_EGCResultInvalidMessage"))]
InvalidMessage = raw::GC_RESULT_INVALID_MESSAGE,
}
impl GcResult {
pub const fn as_raw(self) -> i32 {
self as i32
}
}
#[doc(alias("MsgType_t", "EMsg"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MessageType(u32);
impl MessageType {
#[doc(alias("k_EMsgGCServerHello"))]
pub const SERVER_HELLO: Self = Self::new(raw::SERVER_HELLO);
#[doc(alias("k_EMsgGCServerWelcome"))]
pub const SERVER_WELCOME: Self = Self::new(raw::SERVER_WELCOME);
pub const fn new(id: u32) -> Self {
Self(id & !raw::PROTOBUF_FLAG)
}
pub const fn from_wire(wire: u32) -> (Self, bool) {
(Self::new(wire), wire & raw::PROTOBUF_FLAG != 0)
}
pub const fn id(self) -> u32 {
self.0
}
}
#[derive(Debug, thiserror::Error)]
pub enum SteamClientError {
#[error("could not pin the library implementing the game coordinator: {0}")]
Module(#[from] util::Error),
#[error("the game coordinator is implemented by {0:?}, not Steam's client library")]
NotSteamClient(PathBuf),
}
impl<'s> Server<'s> {
#[doc(alias(
"SteamGameCoordinator",
"SteamInternal_FindOrCreateGameServerInterface"
))]
pub fn game_coordinator(&self) -> Option<GameCoordinator<'s>> {
let ptr = unsafe { raw::game_server_coordinator() }?;
Some(GameCoordinator {
ptr,
_scope: PhantomData,
_not_thread_safe: PhantomData,
})
}
}
fn is_steam_client(name: Option<&OsStr>) -> bool {
let Some(name) = name.and_then(OsStr::to_str) else {
return false;
};
match cfg!(windows) {
true => name.eq_ignore_ascii_case(raw::STEAM_CLIENT_LIBRARY),
false => name == raw::STEAM_CLIENT_LIBRARY,
}
}