diff --git a/Extension/AMBuilder b/Extension/AMBuilder index 3404f77..64a0ae4 100644 --- a/Extension/AMBuilder +++ b/Extension/AMBuilder @@ -13,6 +13,7 @@ project.sources += [ 'ssnatives.cpp', 'swhttp.cpp', 'swhttprequest.cpp', + 'swgchooks.cpp', 'sdk/smsdk_ext.cpp', 'CDetour/detours.cpp', 'asm/asm.c' diff --git a/Extension/extension.cpp b/Extension/extension.cpp index 5fc62eb..9d09578 100644 --- a/Extension/extension.cpp +++ b/Extension/extension.cpp @@ -55,11 +55,13 @@ bool SteamWorks::SDK_OnLoad(char *error, size_t maxlength, bool late) this->pGSHooks = new SteamWorksGSHooks; this->pGSDetours = new SteamWorksGSDetours; this->pSSNatives = new SteamWorksSSNatives; + this->pGCHooks = new SteamWorksGCHooks; return true; } void SteamWorks::SDK_OnUnload() { + delete this->pGCHooks; delete this->pSSNatives; delete this->pGSDetours; delete this->pGSHooks; diff --git a/Extension/extension.h b/Extension/extension.h index 758ba84..ea96b49 100644 --- a/Extension/extension.h +++ b/Extension/extension.h @@ -47,6 +47,7 @@ #include "swforwards.h" #include "gsnatives.h" #include "swgshooks.h" +#include "swgchooks.h" #include "ssnatives.h" #include "swgsdetours.h" #include "swhttp.h" @@ -142,6 +143,7 @@ public: SteamWorksGSDetours *pGSDetours; SteamWorksHTTP *pSWHTTP; SteamWorksHTTPNatives *pSWHTTPNatives; + SteamWorksGCHooks *pGCHooks; }; extern SteamWorks g_SteamWorks; diff --git a/Extension/sdk/smsdk_config.h b/Extension/sdk/smsdk_config.h index 02fdd7e..9ac5f9e 100644 --- a/Extension/sdk/smsdk_config.h +++ b/Extension/sdk/smsdk_config.h @@ -40,7 +40,7 @@ /* Basic information exposed publicly */ #define SMEXT_CONF_NAME "SteamWorks Extension" #define SMEXT_CONF_DESCRIPTION "Exposes SteamWorks functions to Developers" -#define SMEXT_CONF_VERSION "1.2" +#define SMEXT_CONF_VERSION "1.2.1" #define SMEXT_CONF_AUTHOR "Kyle Sanderson" #define SMEXT_CONF_URL "http://AlliedMods.net" #define SMEXT_CONF_LOGTAG "STEAMWORKS" diff --git a/Extension/swgameserver.cpp b/Extension/swgameserver.cpp index dd34170..c097be6 100644 --- a/Extension/swgameserver.cpp +++ b/Extension/swgameserver.cpp @@ -55,6 +55,7 @@ void SteamWorksGameServer::Reset(void) this->m_pStats = NULL; this->m_pHTTP = NULL; this->m_pMatchmaking = NULL; + this->m_pGC = NULL; } ISteamClient *SteamWorksGameServer::GetSteamClient(void) @@ -156,6 +157,22 @@ ISteamMatchmaking *SteamWorksGameServer::GetMatchmaking(void) return this->m_pMatchmaking; } +ISteamGameCoordinator *SteamWorksGameServer::GetGameCoordinator(void) +{ + if (this->m_pGC == NULL && this->GetSteamClient() != NULL) + { + HSteamUser hSteamUser; + HSteamPipe hSteamPipe; + GetUserAndPipe(hSteamUser, hSteamPipe); + + const char *pVersion = STEAMGAMECOORDINATOR_INTERFACE_VERSION; + GetGameSpecificConfigInterface("SteamGameCoordinatorVersion", pVersion); + this->m_pGC = static_cast(this->GetSteamClient()->GetISteamGenericInterface(hSteamUser, hSteamPipe, STEAMGAMECOORDINATOR_INTERFACE_VERSION)); + } + + return this->m_pGC; +} + void SteamWorksGameServer::GetUserAndPipe(HSteamUser &hSteamUser, HSteamPipe &hSteamPipe) { hSteamUser = SteamGameServer_GetHSteamUser(); diff --git a/Extension/swgameserver.h b/Extension/swgameserver.h index 6677c4a..ae56bde 100644 --- a/Extension/swgameserver.h +++ b/Extension/swgameserver.h @@ -19,6 +19,7 @@ #pragma once #include "smsdk_ext.h" #include "steam_gameserver.h" +#include "isteamgamecoordinator.h" class SteamWorksGameServer { @@ -34,6 +35,8 @@ class SteamWorksGameServer ISteamGameServerStats *GetServerStats(void); ISteamHTTP *GetHTTP(void); ISteamMatchmaking *GetMatchmaking(void); + ISteamGameCoordinator *GetGameCoordinator(void); + public: void Reset(void); private: @@ -46,6 +49,7 @@ class SteamWorksGameServer ISteamGameServerStats *m_pStats; ISteamHTTP *m_pHTTP; ISteamMatchmaking *m_pMatchmaking; + ISteamGameCoordinator *m_pGC; bool loaded; }; diff --git a/Extension/swgchooks.cpp b/Extension/swgchooks.cpp new file mode 100644 index 0000000..b156d13 --- /dev/null +++ b/Extension/swgchooks.cpp @@ -0,0 +1,193 @@ +/* + This file is part of SourcePawn SteamWorks. + + SourcePawn SteamWorks is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, as per version 3 of the License. + + SourcePawn SteamWorks is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SourcePawn SteamWorks. If not, see . + + Author: Kyle Sanderson (KyleS). +*/ + +#include "swgchooks.h" + +enum +{ + eUnhooked = 0, + eHooking, + eHooked +}; + +SH_DECL_HOOK3(ISteamGameCoordinator, SendMessage, SH_NOATTRIB, 0, EGCResults, uint32, const void *, uint32); +SH_DECL_HOOK1(ISteamGameCoordinator, IsMessageAvailable, SH_NOATTRIB, 0, bool, uint32 *); +SH_DECL_HOOK4(ISteamGameCoordinator, RetrieveMessage, SH_NOATTRIB, 0, EGCResults, uint32 *, void *, uint32, uint32 *); + +static ISteamGameCoordinator *GetSteamGCPointer() +{ + return g_SteamWorks.pSWGameServer->GetGameCoordinator(); +} + +SteamWorksGCHooks::SteamWorksGCHooks() +{ + this->uHooked = eHooking; + this->pGCSendMsg = forwards->CreateForward("SteamWorks_GCSendMessage", ET_Event, 3, NULL, Param_Cell, Param_String, Param_Cell); + this->pGCMsgAvail = forwards->CreateForward("SteamWorks_GCMsgAvailable", ET_Ignore, 1, NULL, Param_Cell); + this->pGCRetMsg = forwards->CreateForward("SteamWorks_RetreiveMessage", ET_Event, 5, NULL, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_Cell); + + smutils->AddGameFrameHook(OurGCGameFrameHook); +} + +SteamWorksGCHooks::~SteamWorksGCHooks() +{ + this->RemoveHooks(GetSteamGCPointer(), true); + smutils->RemoveGameFrameHook(OurGCGameFrameHook); + forwards->ReleaseForward(this->pGCSendMsg); + forwards->ReleaseForward(this->pGCMsgAvail); + forwards->ReleaseForward(this->pGCRetMsg); +} + +EGCResults SteamWorksGCHooks::SendMessage(uint32 unMsgType, const void *pubData, uint32 cubData) +{ + if (this->pGCSendMsg->GetFunctionCount() == 0) + { + RETURN_META_VALUE(MRES_IGNORED, k_EGCResultOK); + } + + cell_t Result = k_EGCResultOK; + this->pGCSendMsg->PushCell(unMsgType); + this->pGCSendMsg->PushStringEx(reinterpret_cast(const_cast(pubData)), cubData, SM_PARAM_STRING_BINARY | SM_PARAM_STRING_COPY, 0); + this->pGCSendMsg->PushCell(cubData); + this->pGCSendMsg->Execute(&Result); + + if (Result != k_EGCResultOK) + { + if (Result == -1) + { + Result = k_EGCResultOK; + } + + RETURN_META_VALUE(MRES_SUPERCEDE, static_cast(Result)); + } + + RETURN_META_VALUE(MRES_IGNORED, k_EGCResultOK); +} + +bool SteamWorksGCHooks::IsMessageAvailable(uint32_t *pcubMsgSize) +{ + if (this->pGCMsgAvail->GetFunctionCount() == 0) + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + bool res = META_RESULT_ORIG_RET(bool); + if (!res) + { + RETURN_META_VALUE(MRES_IGNORED, false); + } + + uint32_t shill; + if (!pcubMsgSize) + { + SH_CALL(GetSteamGCPointer(), &ISteamGameCoordinator::IsMessageAvailable)(&shill); + pcubMsgSize = &shill; + } + + this->pGCMsgAvail->PushCell(*pcubMsgSize); + this->pGCMsgAvail->Execute(NULL); + RETURN_META_VALUE(MRES_IGNORED, true); +} + +EGCResults SteamWorksGCHooks::RetrieveMessage(uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize) +{ + if (this->pGCRetMsg->GetFunctionCount() == 0) + { + RETURN_META_VALUE(MRES_IGNORED, k_EGCResultOK); + } + + /* Don't trust a bitch, except for 2GD. https://www.youtube.com/watch?v=MgePh_YJgrc */ + cell_t Result; + EGCResults res = SH_CALL(GetSteamGCPointer(), &ISteamGameCoordinator::RetrieveMessage)(punMsgType, pubDest, cubDest, pcubMsgSize); + if (punMsgType) + this->pGCRetMsg->PushCell(*punMsgType); + else + this->pGCRetMsg->PushCell(0); + + if (pubDest) + this->pGCSendMsg->PushStringEx(reinterpret_cast(pubDest), cubDest, SM_PARAM_STRING_BINARY | SM_PARAM_STRING_COPY, 0); + else + this->pGCSendMsg->PushStringEx(const_cast(""), 1, SM_PARAM_STRING_BINARY | SM_PARAM_STRING_COPY, 0); + + this->pGCRetMsg->PushCell(cubDest); + + if (pcubMsgSize) + this->pGCRetMsg->PushCell(*pcubMsgSize); + else + this->pGCRetMsg->PushCell(0); + + this->pGCRetMsg->Execute(&Result); + + if (Result != k_EGCResultOK) + { + if (Result == -1) + { + Result = k_EGCResultOK; + } + + RETURN_META_VALUE(MRES_SUPERCEDE, static_cast(Result)); + } + + RETURN_META_VALUE(MRES_IGNORED, k_EGCResultOK); +} + +void SteamWorksGCHooks::AddHooks(ISteamGameCoordinator *pGC) +{ + if (this->uHooked == eHooked || pGC == NULL) + { + return; + } + + this->uHooked = eHooked; + SH_ADD_HOOK(ISteamGameCoordinator, SendMessage, pGC, SH_MEMBER(this, &SteamWorksGCHooks::SendMessage), false); + SH_ADD_HOOK(ISteamGameCoordinator, IsMessageAvailable, pGC, SH_MEMBER(this, &SteamWorksGCHooks::IsMessageAvailable), true); + SH_ADD_HOOK(ISteamGameCoordinator, RetrieveMessage, pGC, SH_MEMBER(this, &SteamWorksGCHooks::RetrieveMessage), false); +} + +void SteamWorksGCHooks::RemoveHooks(ISteamGameCoordinator *pGC, bool destroyed) +{ + if (this->uHooked != eHooked || pGC == NULL) + { + return; + } + + SH_REMOVE_HOOK(ISteamGameCoordinator, SendMessage, pGC, SH_MEMBER(this, &SteamWorksGCHooks::SendMessage), false); + SH_REMOVE_HOOK(ISteamGameCoordinator, IsMessageAvailable, pGC, SH_MEMBER(this, &SteamWorksGCHooks::IsMessageAvailable), true); + SH_REMOVE_HOOK(ISteamGameCoordinator, RetrieveMessage, pGC, SH_MEMBER(this, &SteamWorksGCHooks::RetrieveMessage), false); + + if (destroyed) + { + this->uHooked = eUnhooked; + return; + } + + this->uHooked = eHooking; + smutils->AddGameFrameHook(OurGameFrameHook); +} + +void OurGCGameFrameHook(bool simulating) /* What we do for SDK independence. */ +{ + ISteamGameCoordinator *pGC = GetSteamGCPointer(); + if (pGC == NULL) + { + return; + } + + g_SteamWorks.pGCHooks->AddHooks(pGC); + smutils->RemoveGameFrameHook(OurGCGameFrameHook); +} diff --git a/Extension/swgchooks.h b/Extension/swgchooks.h new file mode 100644 index 0000000..53071ab --- /dev/null +++ b/Extension/swgchooks.h @@ -0,0 +1,50 @@ +/* + This file is part of SourcePawn SteamWorks. + + SourcePawn SteamWorks is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, as per version 3 of the License. + + SourcePawn SteamWorks is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with SourcePawn SteamWorks. If not, see . + + Author: Kyle Sanderson (KyleS). +*/ + +#pragma once +#include "steam_gameserver.h" +#include "isteamgamecoordinator.h" + +#include "smsdk_ext.h" +#include "sourcehook.h" + +class SteamWorksGCHooks +{ + public: + SteamWorksGCHooks(); + ~SteamWorksGCHooks(); + + public: + void AddHooks(ISteamGameCoordinator *pGC); + void RemoveHooks(ISteamGameCoordinator *pGC, bool destroyed = false); + + public: + EGCResults SendMessage(uint32 unMsgType, const void *pubData, uint32 cubData); + bool IsMessageAvailable(uint32_t *pcubMsgSize); + EGCResults RetrieveMessage(uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize); + + private: + IForward *pGCSendMsg; + IForward *pGCMsgAvail; + IForward *pGCRetMsg; + unsigned char uHooked; +}; + +void OurGCGameFrameHook(bool simulating); + +#include "extension.h" diff --git a/Pawn/includes/SteamWorks.inc b/Pawn/includes/SteamWorks.inc index 2fcd8e5..6c1649c 100644 --- a/Pawn/includes/SteamWorks.inc +++ b/Pawn/includes/SteamWorks.inc @@ -175,6 +175,16 @@ enum EHTTPStatusCode k_EHTTPStatusCode5xxUnknown = 599, }; +/* list of possible return values from the ISteamGameCoordinator API */ +enum EGCResults +{ + k_EGCResultOK = 0, + k_EGCResultNoMessage = 1, // There is no message in the queue + k_EGCResultBufferTooSmall = 2, // The buffer is too small for the requested message + k_EGCResultNotLoggedOn = 3, // The client is not logged onto Steam + k_EGCResultInvalidMessage = 4, // Something was wrong with the message being sent with SendMessage +}; + native bool:SteamWorks_IsVACEnabled(); native bool:SteamWorks_GetPublicIP(ipaddr[4]); native SteamWorks_GetPublicIPCell(); @@ -258,6 +268,10 @@ forward SteamWorks_TokenRequested(String:sToken[], maxlen); forward SteamWorks_OnClientGroupStatus(authid, groupid, bool:isMember, bool:isOfficer); +forward EGCResults:SteamWorks_GCSendMessage(unMsgType, const String:pubData[], cubData); +forward SteamWorks_GCMsgAvailable(cubData); +forward EGCResults:SteamWorks_RetreiveMessage(punMsgType, const String:pubDest[], cubDest, pcubMsgSize); + public Extension:__ext_SteamWorks = { name = "SteamWorks",