diff --git a/buildbot/PackageScript b/buildbot/PackageScript index 014a2f1..df2c999 100644 --- a/buildbot/PackageScript +++ b/buildbot/PackageScript @@ -40,8 +40,8 @@ for cxx_task in Extension.extensions: pdblog.write(cxx_task.debug.path + '\n') pdblog.close() -CopyFiles('connect.games.txt', folders['addons/sourcemod/gamedata']) +CopyFiles('connect2.games.txt', folders['addons/sourcemod/gamedata']) CopyFiles('connect.inc', folders['addons/sourcemod/scripting/include']) CopyFiles('connect.sp', folders['addons/sourcemod/scripting']) -debug_info = [] \ No newline at end of file +debug_info = [] diff --git a/connect.games.txt b/connect.games.txt deleted file mode 100644 index ac9ab2b..0000000 --- a/connect.games.txt +++ /dev/null @@ -1,87 +0,0 @@ -"Games" -{ - "#default" - { - "#supported" - { - "engine" "dods" - "engine" "css" - "engine" "hl2dm" - "engine" "tf2" - } - - "Offsets" - { - "ISteamGameServer__BeginAuthSession" - { - "linux" "26" - "linux64" "26" - "windows" "26" - "windows64" "26" - } - - "ISteamGameServer__EndAuthSession" - { - "linux" "27" - "linux64" "27" - "windows" "27" - "windows64" "27" - } - - "CheckMasterServerRequestRestart_Steam3ServerFuncOffset" - { - "windows" "240" - "windows64" "299" - } - } - - "Signatures" - { - "Steam3Server" - { - "library" "engine" - "linux" "@_Z12Steam3Serverv" - "linux64" "@_Z12Steam3Serverv" - } - - "CBaseServer__ConnectClient" - { - "library" "engine" - "linux" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i" - "linux64" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i" - "windows" "\x55\x8B\xEC\x81\xEC\x24\x05\x00\x00\x53\x56\x57\x68\x2A\x2A\x2A\x2A" - "windows64" "\x48\x89\x5C\x24\x2A\x44\x89\x4C\x24\x2A\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x81\xEC\x80\x05\x00\x00" - } - - "CBaseServer__RejectConnection" - { - "library" "engine" - "linux" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc" - "linux64" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc" - "windows" "\x55\x8B\xEC\x81\xEC\x04\x05\x00\x00\x57" - "windows64" "\x48\x89\x5C\x24\x2A\x48\x89\x6C\x24\x2A\x48\x89\x74\x24\x2A\x57\x48\x81\xEC\x50\x05\x00\x00" - } - - "CBaseServer__CheckMasterServerRequestRestart" - { - // "%cMasterRequestRestart" - "library" "engine" - "windows" "\x55\x8B\xEC\x83\xEC\x1C\x53\x57\x33\xD2" - "windows64" "\x4C\x8B\xDC\x49\x89\x5B\x2A\x49\x89\x6B\x2A\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x60\x48\x8B\x05\x2A\x2A\x2A\x2A\x48\x8D\x1D" - } - } - } - "tf" - { - "Signatures" - { - "CBaseServer__CheckMasterServerRequestRestart" - { - "library" "engine" - // "%cMasterRequestRestart" - "windows" "\x55\x8B\xEC\x83\xEC\x18\x53\x57\x33\xD2\x8B\xF9\x8B\x0D\x2A\x2A\x2A\x2A\x89\x55\xE8\x89\x55\xEC\x89\x55\xF4\x8B\x41\x08" - "windows64" "\x4C\x8B\xDC\x49\x89\x5B\x10\x49\x89\x6B\x18\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x60\x48\x8B\x05\x2A\x83\x20\x00" - } - } - } -} diff --git a/connect.inc b/connect.inc index c2e27dd..3219ca9 100644 --- a/connect.inc +++ b/connect.inc @@ -4,7 +4,17 @@ #define _connect_included +enum EConnect +{ + k_OnClientPreConnectEx_Reject = 0, + k_OnClientPreConnectEx_Accept = 1, + k_OnClientPreConnectEx_Async = -1 +}; + + forward bool OnClientPreConnectEx(const char[] name, char password[255], const char[] ip, const char[] steamID, char rejectReason[255]); +native bool ClientPreConnectEx(const char[] sSteam32ID, EConnect RetVal, char sRejectReason[255]); +native bool SteamClientAuthenticated(const char[] sSteam32ID); public Extension __ext_Connect = { diff --git a/connect2.games.txt b/connect2.games.txt new file mode 100644 index 0000000..ae3485a --- /dev/null +++ b/connect2.games.txt @@ -0,0 +1,147 @@ +"Games" +{ + "#default" + { + "#supported" + { + "engine" "dods" + "engine" "css" + "engine" "hl2dm" + "engine" "tf2" + } + + "Offsets" + { + "ISteamGameServer__BeginAuthSession" + { + "linux" "26" + "linux64" "26" + "windows" "26" + "windows64" "26" + } + + "ISteamGameServer__EndAuthSession" + { + "linux" "27" + "linux64" "27" + "windows" "27" + "windows64" "27" + } + + "CheckMasterServerRequestRestart_Steam3ServerFuncOffset" + { + "windows" "240" + "windows64" "299" + } + } + + "Signatures" + { + "Steam3Server" + { + "library" "engine" + "linux" "@_Z12Steam3Serverv" + "linux64" "@_Z12Steam3Serverv" + } + + "CBaseServer__ConnectClient" + { + "library" "engine" + "linux" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i" + "linux64" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i" + "windows" "\x55\x8B\xEC\x81\xEC\x24\x05\x00\x00\x53\x56\x57\x68\x2A\x2A\x2A\x2A" + "windows64" "\x48\x89\x5C\x24\x2A\x44\x89\x4C\x24\x2A\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x81\xEC\x80\x05\x00\x00" + } + + "CBaseServer__CheckChallengeType" + { + "library" "engine" + "linux" "@_ZN11CBaseServer18CheckChallengeTypeEP11CBaseClientiR8netadr_siPKcii" + "mac" "@_ZN11CBaseServer18CheckChallengeTypeEP11CBaseClientiR8netadr_siPKcii" + "windows" "\x55\x8B\xEC\x83\xEC\x14\x56\x57\x8B\x7D\x14\x8B\xF1" + } + + "CBaseServer__RejectConnection" + { + "library" "engine" + "linux" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc" + "linux64" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc" + "windows" "\x55\x8B\xEC\x81\xEC\x04\x05\x00\x00\x57" + "windows64" "\x48\x89\x5C\x24\x2A\x48\x89\x6C\x24\x2A\x48\x89\x74\x24\x2A\x57\x48\x81\xEC\x50\x05\x00\x00" + } + + "CBaseClient__SetSteamID" + { + "library" "engine" + "linux" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID" + "linux64" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID" + "windows" "\x55\x8B\xEC\x56\x8B\xF1\x57\x8B\x7D\x08\x8D\x4E\x04" + "windows64" "\x48\x89\x5C\x24\x2A\x57\x48\x83\xEC\x20\x48\x8B\x02\x48\x8B\xD9\x48\x89\x41" + } + + "CBaseServer__CheckMasterServerRequestRestart" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xEC\x1C\x53\x57\x33\xD2" + "windows64" "\x4C\x8B\xDC\x49\x89\x5B\x2A\x49\x89\x6B\x2A\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x60\x48\x8B\x05\x2A\x2A\x2A\x2A\x48\x8D\x1D" + } + + "NET_SendPacket" + { + "library" "engine" + "linux" "@_Z14NET_SendPacketP11INetChanneliRK8netadr_sPKhiP8bf_writeb" + } + + "NET_CheckCleanupFakeIPConnection" + { + "library" "engine" + "linux" "@_Z32NET_CheckCleanupFakeIPConnectioniRK8netadr_s" + } + + "CSteam3Server__OnValidateAuthTicketResponse" + { + "library" "engine" + "linux" "@_ZN13CSteam3Server28OnValidateAuthTicketResponseEP28ValidateAuthTicketResponse_t" + } + "s_queryRateChecker" + { + "library" "engine" + "linux" "@_ZL18s_queryRateChecker" + } + "CIPRateLimit__CheckIP" + { + "library" "engine" + "linux" "@_ZN12CIPRateLimit7CheckIPE8netadr_s" + } + "CBaseServer__ValidChallenge" + { + "library" "engine" + "linux" "@_ZN11CBaseServer14ValidChallengeER8netadr_si" + } + + "CBaseServer__InactivateClients" + { + "library" "engine" + "linux" "@_ZN11CBaseServer17InactivateClientsEv" + } + + "net_sockets" + { + "library" "engine" + "linux" "@_ZL11net_sockets" + } + + "hltv" + { + "library" "engine" + "linux" "@hltv" + } + + "net_time" + { + "library" "engine" + "linux" "@net_time" + } + } + } +} diff --git a/extension/extension.cpp b/extension/extension.cpp index 60a2c9e..9e0e91d 100644 --- a/extension/extension.cpp +++ b/extension/extension.cpp @@ -19,8 +19,10 @@ #include "extension.h" #include "CDetour/detours.h" - +#include "sm_namehashset.h" #include "steam/steamclientpublic.h" +#include "steam/isteamclient.h" +#include #include Connect g_connect; @@ -28,6 +30,12 @@ Connect g_connect; SMEXT_LINK(&g_connect); ConVar connectVersion("connect_version", SMEXT_CONF_VERSION, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, SMEXT_CONF_DESCRIPTION " Version"); +ConVar g_SvLogging("sv_connect_logging", "0", FCVAR_NOTIFY, "Log connection checks."); +ConVar g_SvNoSteam("sv_nosteam", "0", FCVAR_NOTIFY, "Disable steam validation and force steam authentication."); +ConVar g_SvForceSteam("sv_forcesteam", "0", FCVAR_NOTIFY, "Force steam authentication."); +ConVar g_SvGameDesc("sv_gamedesc_override", "default", FCVAR_NOTIFY, "Overwrite the default game description. Set to 'default' to keep default description."); +ConVar g_SvMapName("sv_mapname_override", "default", FCVAR_NOTIFY, "Overwrite the map name. Set to 'default' to keep default name."); + IGameConfig *g_pGameConf = NULL; IForward *g_pConnectForward = NULL; @@ -37,60 +45,57 @@ class CBaseServer; typedef enum EAuthProtocol { - k_EAuthProtocolWONCertificate = 1, - k_EAuthProtocolHashedCDKey = 2, - k_EAuthProtocolSteam = 3, + k_EAuthProtocolWONCertificate = 1, + k_EAuthProtocolHashedCDKey = 2, + k_EAuthProtocolSteam = 3, } EAuthProtocol; -#if SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2 -typedef enum EBeginAuthSessionResult -{ - k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID. - k_EBeginAuthSessionResultInvalidTicket = 1, // Ticket is not valid. - k_EBeginAuthSessionResultDuplicateRequest = 2, // A ticket has already been submitted for this steamID - k_EBeginAuthSessionResultInvalidVersion = 3, // Ticket is from an incompatible interface version - k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game - k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired -} EBeginAuthSessionResult; -#endif +/* + k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID. + k_EBeginAuthSessionResultInvalidTicket = 1, // Ticket is not valid. + k_EBeginAuthSessionResultDuplicateRequest = 2, // A ticket has already been submitted for this steamID + k_EBeginAuthSessionResultInvalidVersion = 3, // Ticket is from an incompatible interface version + k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game + k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired +*/ typedef struct netadr_s { private: - typedef enum - { - NA_NULL = 0, - NA_LOOPBACK, - NA_BROADCAST, - NA_IP, - } netadrtype_t; + typedef enum + { + NA_NULL = 0, + NA_LOOPBACK, + NA_BROADCAST, + NA_IP, + } netadrtype_t; public: - netadrtype_t type; - unsigned char ip[4]; - unsigned short port; + netadrtype_t type; + unsigned char ip[4]; + unsigned short port; } netadr_t; const char *CSteamID::Render() const { - static char szSteamID[64]; - V_snprintf(szSteamID, sizeof(szSteamID), "STEAM_0:%u:%u", this->GetAccountID() & 1, this->GetAccountID() >> 1); - return szSteamID; + static char szSteamID[64]; + V_snprintf(szSteamID, sizeof(szSteamID), "STEAM_0:%u:%u", this->GetAccountID() & 1, this->GetAccountID() >> 1); + return szSteamID; } class ISteamGameServer; class CSteam3Server { public: - void *m_pSteamClient; - ISteamGameServer* m_pSteamGameServer; - void *m_pSteamGameServerUtils; - void *m_pSteamGameServerNetworking; - void *m_pSteamGameServerStats; - void *m_pSteamHTTP; - void *m_pSteamInventory; - void *m_pSteamUGC; - void *m_pSteamApps; + void *m_pSteamClient; + ISteamGameServer* m_pSteamGameServer; + void *m_pSteamGameServerUtils; + void *m_pSteamGameServerNetworking; + void *m_pSteamGameServerStats; + void *m_pSteamHTTP; + void *m_pSteamInventory; + void *m_pSteamUGC; + void *m_pSteamApps; } *g_pSteam3Server; typedef CSteam3Server *(*Steam3ServerFunc)(); @@ -99,65 +104,68 @@ class FEmptyClass {}; template union MFPHack { private: - void* addr; + void* addr; public: - MFPHack(void* addr) - { - this->addr = addr; - } + MFPHack(void* addr) + { + this->addr = addr; + } - template - MFPHack(RetType (randomclass::*mfp)(Args...)) - { - union - { - RetType (randomclass::*ptr)(Args...); - struct - { - void* addr; + template + MFPHack(RetType (randomclass::*mfp)(Args...)) + { + union + { + RetType (randomclass::*ptr)(Args...); + struct + { + void* addr; #ifdef __linux__ - intptr_t adjustor; + intptr_t adjustor; #endif - } details; - } u; - u.ptr = mfp; - this->addr = u.details.addr; - } + } details; + } u; + u.ptr = mfp; + this->addr = u.details.addr; + } - void SetAddress(void* addr) - { - this->addr = addr; - } + void SetAddress(void* addr) + { + this->addr = addr; + } - void* GetAddress() - { - return this->addr; - } + void* GetAddress() + { + return this->addr; + } - RetType operator()(classcall* ptrThis, Args... args) - { - union - { - RetType (FEmptyClass::*ptr)(Args...); - struct - { - void* addr; + RetType operator()(classcall* ptrThis, Args... args) + { + union + { + RetType (FEmptyClass::*ptr)(Args...); + struct + { + void* addr; #ifdef __linux__ - intptr_t adjustor; + intptr_t adjustor; #endif - } details; - } u; + } details; + } u; - u.details.addr = addr; + u.details.addr = addr; #ifdef __linux__ - u.details.adjustor = 0; + u.details.adjustor = 0; #endif - return (((FEmptyClass*)ptrThis)->*u.ptr)(args...); - } + return (((FEmptyClass*)ptrThis)->*u.ptr)(args...); + } }; CDetour* detourCBaseServer__ConnectClient = nullptr; +CDetour *g_Detour_CSteam3Server__OnValidateAuthTicketResponse = NULL; + bool g_bSuppressBeginAuthSession = false; + CSteamID g_lastClientSteamID; const void* g_lastAuthTicket; int g_lastcbAuthTicket; @@ -171,235 +179,588 @@ MFPHack g_pEndAuthSession(nullptr); CSteam3Server *Steam3Server() { - if (!g_pSteam3ServerFunc) - return NULL; + if (!g_pSteam3ServerFunc) + return NULL; - return g_pSteam3ServerFunc(); + return g_pSteam3ServerFunc(); } SH_DECL_MANUALHOOK3(MHook_BeginAuthSession, 0, 0, 0, EBeginAuthSessionResult, const void *, int, CSteamID); EBeginAuthSessionResult Hook_BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID) { - if (!g_bSuppressBeginAuthSession) - { - RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultOK); - } - g_bSuppressBeginAuthSession = false; + if (!g_bSuppressBeginAuthSession) + { + //g_pSM->LogMessage(myself, "inside Hook_ BeginAuthSession 1"); + RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultOK); + } + g_bSuppressBeginAuthSession = false; - if (strcmp(steamID.Render(), g_lastClientSteamID.Render()) == 0 - && g_lastAuthTicket == pAuthTicket - && g_lastcbAuthTicket == cbAuthTicket) - { - // Let the server know everything is fine - // g_pSM->LogMessage(myself, "You alright ;)"); - RETURN_META_VALUE(MRES_SUPERCEDE, k_EBeginAuthSessionResultOK); - } + if (strcmp(steamID.Render(), g_lastClientSteamID.Render()) == 0 + && g_lastAuthTicket == pAuthTicket + && g_lastcbAuthTicket == cbAuthTicket) + { + // Let the server know everything is fine + //g_pSM->LogMessage(myself, "inside Hook_ BeginAuthSession 2"); + RETURN_META_VALUE(MRES_SUPERCEDE, k_EBeginAuthSessionResultOK); + } - RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultDuplicateRequest); + //g_pSM->LogMessage(myself, "inside Hook_ BeginAuthSession 3"); + RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultDuplicateRequest); } +struct ValidateAuthTicketResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 43 }; + CSteamID m_SteamID; + EAuthSessionResponse m_eAuthSessionResponse; + CSteamID m_OwnerSteamID; // different from m_SteamID if borrowed + //in dewey voice "can a nigga borrow a m_SteamID?" + //the teacher "How is a nigga gonna borrow a m_SteamID? nigga is you gonna give it back?" +}; + +class ConnectClientStorage +{ +public: + void* pThis; + + netadr_t address; + int nProtocol; + int iChallenge; + int iClientChallenge; + int nAuthProtocol; + char pchName[256]; + char pchPassword[256]; + char pCookie[256]; + int cbCookie; + IClient *pClient; + + uint64 ullSteamID; + ValidateAuthTicketResponse_t ValidateAuthTicketResponse; + bool GotValidateAuthTicketResponse; + bool SteamLegal; + bool SteamAuthFailed; + + void *pvTicket; + int cbTicket; + + ConnectClientStorage() { } + ConnectClientStorage(netadr_t address, int nProtocol, int iChallenge, int iClientChallenge, int nAuthProtocol, const char *pchName, const char *pchPassword, const char *pCookie, int cbCookie) + { + this->address = address; + this->nProtocol = nProtocol; + this->iChallenge = iChallenge; + this->iClientChallenge = iClientChallenge; + this->nAuthProtocol = nAuthProtocol; + + V_strncpy(this->pchName, pchName, 255); + V_strncpy(this->pchPassword, pchPassword, 255); + memcpy(this->pCookie, pCookie, cbCookie); + + this->cbCookie = cbCookie; + this->pClient = NULL; + this->GotValidateAuthTicketResponse = false; + this->SteamLegal = false; + this->SteamAuthFailed = false; + + // Calculate and store the ticket pointer + this->pvTicket = (void *)((intptr_t)this->pCookie + sizeof(uint64)); + this->cbTicket = cbCookie - sizeof(uint64); + } +}; +StringHashMap g_ConnectClientStorage; + +DETOUR_DECL_MEMBER1(CSteam3Server__OnValidateAuthTicketResponse, int, ValidateAuthTicketResponse_t *, pResponse) +{ + char aSteamID[32]; + V_strncpy(aSteamID, pResponse->m_SteamID.Render(), 32); + bool SteamLegal = pResponse->m_eAuthSessionResponse == k_EAuthSessionResponseOK; + bool force = g_SvNoSteam.GetInt() || g_SvForceSteam.GetInt(); + + if(!SteamLegal && force) + { + pResponse->m_eAuthSessionResponse = k_EAuthSessionResponseOK; + } + + ConnectClientStorage Storage; + if(g_ConnectClientStorage.retrieve(aSteamID, &Storage)) + { + if(!Storage.GotValidateAuthTicketResponse) + { + Storage.GotValidateAuthTicketResponse = true; + Storage.ValidateAuthTicketResponse = *pResponse; + Storage.SteamLegal = SteamLegal; + g_ConnectClientStorage.replace(aSteamID, Storage); + } + } + + return DETOUR_MEMBER_CALL(CSteam3Server__OnValidateAuthTicketResponse)(pResponse); +} + +void *g_pLastHookedSteamGameServer = NULL; +int g_nBeginAuthSessionOffset = 0; + DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, int, nProtocol, int, iChallenge, int, iClientChallenge, int, nAuthProtocol, const char *, pchName, const char *, pchPassword, const char *, pCookie, int, cbCookie) { - if (nAuthProtocol != k_EAuthProtocolSteam) - { - // This is likely a SourceTV client, we don't want to interfere here. - return DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); - } + if (nAuthProtocol != k_EAuthProtocolSteam) + { + // This is likely a SourceTV client, we don't want to interfere here. + return DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); + } - if (pCookie == NULL || (size_t)cbCookie < sizeof(uint64)) - { - g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectInvalidSteamCertLen"); - return NULL; - } + if (g_pSteam3Server->m_pSteamGameServer != g_pLastHookedSteamGameServer) + { + //the hook tends to die, hence its reapplied whenever needed. thats probably very dumb. + //does however seem to stop "S3: Client connected with invalid ticket:" for nosteamers. + //g_pSM->LogMessage(myself, "Steam GameServer pointer changed! Old: %p, New: %p", g_pLastHookedSteamGameServer, g_pSteam3Server->m_pSteamGameServer); + if (g_pLastHookedSteamGameServer != NULL) + { + // Remove old hook if it exists + SH_REMOVE_MANUALHOOK(MHook_BeginAuthSession, g_pLastHookedSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true); + } - auto steamGameServer = Steam3Server()->m_pSteamGameServer; + // Add hook to the new object + SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, g_nBeginAuthSessionOffset, 0, 0); + SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true); - char ipString[30]; - V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", address.ip[0], address.ip[1], address.ip[2], address.ip[3]); - V_strncpy(passwordBuffer, pchPassword, 255); - uint64 ullSteamID = *(uint64 *)pCookie; + g_pLastHookedSteamGameServer = g_pSteam3Server->m_pSteamGameServer; + } - void *pvTicket = (void *)((intptr_t)pCookie + sizeof(uint64)); - int cbTicket = cbCookie - sizeof(uint64); + if (pCookie == NULL || (size_t)cbCookie < sizeof(uint64)) + { + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectInvalidSteamCertLen"); + return NULL; + } - g_lastClientSteamID = CSteamID(ullSteamID); - g_lastcbAuthTicket = cbTicket; - g_lastAuthTicket = pvTicket; + auto steamGameServer = Steam3Server()->m_pSteamGameServer; - // Validate steam ticket - EBeginAuthSessionResult result = g_pBeginAuthSession(steamGameServer, pvTicket, cbTicket, g_lastClientSteamID); - if (result != k_EBeginAuthSessionResultOK) - { - g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectSteam"); - return NULL; - } + char ipString[30]; + V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", address.ip[0], address.ip[1], address.ip[2], address.ip[3]); + V_strncpy(passwordBuffer, pchPassword, 255); + uint64 ullSteamID = *(uint64 *)pCookie; - char rejectReason[255]; + g_lastClientSteamID = CSteamID(ullSteamID); + void *pvTicket = (void *)((intptr_t)pCookie + sizeof(uint64)); + int cbTicket = cbCookie - sizeof(uint64); - g_pConnectForward->PushString(pchName); - g_pConnectForward->PushStringEx(passwordBuffer, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); - g_pConnectForward->PushString(ipString); - g_pConnectForward->PushString(g_lastClientSteamID.Render()); - g_pConnectForward->PushStringEx(rejectReason, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + char aSteamID[32]; + V_strncpy(aSteamID, g_lastClientSteamID.Render(), 32); - cell_t retVal = 1; - g_pConnectForward->Execute(&retVal); + // If client is in async state remove the old object and fake an async retVal + // This can happen if the async ClientPreConnectEx takes too long to be called + // and the client auto-retries. + bool AsyncWaiting = false; + bool ExistingSteamid = false; + bool SkipEndAuthSession = false; + EBeginAuthSessionResult result; + ConnectClientStorage Storage(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); - if (retVal == 0) - { - g_pEndAuthSession(steamGameServer, g_lastClientSteamID); - g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, rejectReason); - return NULL; - } + if (g_ConnectClientStorage.retrieve(aSteamID, &Storage)) + { + ExistingSteamid = true; + g_ConnectClientStorage.remove(aSteamID); + if (!Storage.SteamAuthFailed) + { + g_pSM->LogMessage(myself, "inside clientstorage about to do endauthsession for steamID: %s", aSteamID); + g_pEndAuthSession(steamGameServer, g_lastClientSteamID); + } - pchPassword = passwordBuffer; + // Only wait for async on auto-retry, manual retries should go through the full chain + // Don't want to leave the client waiting forever if something breaks in the async forward + if (Storage.iClientChallenge == iClientChallenge) + { + AsyncWaiting = true; - g_bSuppressBeginAuthSession = true; - auto client = DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); - g_bSuppressBeginAuthSession = false; - if (client == nullptr) - { - g_pEndAuthSession(steamGameServer, g_lastClientSteamID); - } - return client; + //reject async nosteamers (althought this statement is not reached even as async nosteamers are rejected with invalid ticket) + if (Storage.SteamAuthFailed) + { + //we are only here in async state when server is 64/64. + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "No slots for nosteamers. server is full"); + return NULL; + } + //async steam player with a clientstorage, use the ticket from the previous clientstorage. + g_lastcbAuthTicket = Storage.cbTicket; + g_lastAuthTicket = Storage.pvTicket; + result = g_pBeginAuthSession(steamGameServer, Storage.pvTicket, Storage.cbTicket, g_lastClientSteamID); + } + else + { + //client did a manual retry, + if (Storage.SteamAuthFailed) + { + //its a nosteamer so dont use the clientstorage ticket + g_lastcbAuthTicket = cbTicket; + g_lastAuthTicket = pvTicket; + SkipEndAuthSession = true; + result = k_EBeginAuthSessionResultInvalidTicket; + } + else + { + //create a new client storage for the steamplayer. + Storage = ConnectClientStorage(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); + + //use the ticket from the new clientstorage. + g_lastcbAuthTicket = Storage.cbTicket; + g_lastAuthTicket = Storage.pvTicket; + result = g_pBeginAuthSession(steamGameServer, Storage.pvTicket, Storage.cbTicket, g_lastClientSteamID); + } + } + } + else + { + //nosteamer or steam player without clientstorage, + g_lastcbAuthTicket = cbTicket; + g_lastAuthTicket = pvTicket; + + //NOTE: if the first connecting client on a map is in k_OnClientPreConnectEx_Reject then this beginAuthSession call can crash the server + //however if the first connecting client instead is in k_OnClientPreConnectEx_Accept then the after following + //k_OnClientPreConnectEx_Reject will work fine. Ex_Accept is always used until the server is full, hence should Ex_Reject + //never be the first to connect on a new map. + result = g_pBeginAuthSession(steamGameServer, pvTicket, cbTicket, g_lastClientSteamID); + } + if (!SkipEndAuthSession) + { + g_pEndAuthSession(steamGameServer, g_lastClientSteamID); //xen said engine might start its own authsession, so end ours here. + } + + bool NoSteam = g_SvNoSteam.GetInt(); + bool SteamAuthFailed = false; + //g_pSM->LogMessage(myself, "g_pBeginAuthSession result: %d", result); + + if (result != k_EBeginAuthSessionResultOK) + { + if (!NoSteam) + { + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectSteam"); + return NULL; + } + Storage.SteamAuthFailed = SteamAuthFailed = true; + } + + if(ExistingSteamid && !AsyncWaiting) + { + // Another player trying to spoof a Steam ID or game crashed? + if(memcmp(address.ip, Storage.address.ip, sizeof(address.ip)) != 0) + { + // Reject NoSteam players + if(SteamAuthFailed) + { + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Steam ID already in use."); + return NULL; + } + + // Kick existing player + if(Storage.pClient) + { + Storage.pClient->Disconnect("Same Steam ID connected."); + } + else + { + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Please try again later."); + return NULL; + } + } + } + + char rejectReason[255]; + cell_t retVal = 1; + + if(AsyncWaiting) + { + retVal = -1; // Fake async return code when waiting for async call + } + else + { + g_pConnectForward->PushString(pchName); + g_pConnectForward->PushStringEx(passwordBuffer, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + g_pConnectForward->PushString(ipString); + g_pConnectForward->PushString(g_lastClientSteamID.Render()); + g_pConnectForward->PushStringEx(rejectReason, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + g_pConnectForward->Execute(&retVal); + } + + pchPassword = passwordBuffer; + // k_OnClientPreConnectEx_Reject + if (retVal == 0) + { + g_ConnectClientStorage.remove(aSteamID); + if (!Storage.SteamAuthFailed) + { + //calling this on nosteamers does sometimes cause a server explosion. + //but not when creating a nosteamer with their first ticket + g_pSM->LogMessage(myself, "k_OnClientPreConnectEx_Reject reached about to do endAuthSession. steamID: %s", aSteamID); + g_pEndAuthSession(steamGameServer, g_lastClientSteamID); + } + g_pSM->LogMessage(myself, "k_OnClientPreConnectEx_Reject reached about to do g_pRejectConnectionFunc. steamID: %s", aSteamID); + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, rejectReason); + return NULL; + } + + Storage.pThis = this; + Storage.ullSteamID = ullSteamID; + Storage.SteamAuthFailed = SteamAuthFailed; + + //puts the storage in the StringHashMap + if(!g_ConnectClientStorage.replace(aSteamID, Storage)) + { + g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Internal error."); + return NULL; + } + + // k_OnClientPreConnectEx_Async + if (retVal == -1) + { + return NULL; + } + + // k_OnClientPreConnectEx_Accept + + g_bSuppressBeginAuthSession = true; + auto client = DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie); + g_bSuppressBeginAuthSession = false; + + Storage.pClient = client; + g_ConnectClientStorage.replace(aSteamID, Storage); + + if (client && SteamAuthFailed) + { + ValidateAuthTicketResponse_t Response; + Response.m_SteamID = g_lastClientSteamID; + Response.m_eAuthSessionResponse = k_EAuthSessionResponseAuthTicketInvalid; //nosteamer monkeys + Response.m_OwnerSteamID = Response.m_SteamID; + DETOUR_MEMBER_MCALL_CALLBACK(CSteam3Server__OnValidateAuthTicketResponse, g_pSteam3Server)(&Response); + } + return client; } + bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late) { - char conf_error[255] = ""; - if (!gameconfs->LoadGameConfigFile("connect.games", &g_pGameConf, conf_error, sizeof(conf_error))) - { - if (conf_error[0]) - { - snprintf(error, maxlen, "Could not read connect.games.txt: %s\n", conf_error); - } - return false; - } + char conf_error[255] = ""; + if (!gameconfs->LoadGameConfigFile("connect2.games", &g_pGameConf, conf_error, sizeof(conf_error))) + { + if (conf_error[0]) + { + snprintf(error, maxlen, "Could not read connect2.games.txt: %s\n", conf_error); + } + return false; + } - void* addr; - if (!g_pGameConf->GetMemSig("CBaseServer__RejectConnection", &addr) || addr == nullptr) - { - snprintf(error, maxlen, "Failed to find CBaseServer__RejectConnection function.\n"); - return false; - } - g_pRejectConnectionFunc.SetAddress(addr); + void* addr; + if (!g_pGameConf->GetMemSig("CBaseServer__RejectConnection", &addr) || addr == nullptr) + { + snprintf(error, maxlen, "Failed to find CBaseServer__RejectConnection function.\n"); + return false; + } + g_pRejectConnectionFunc.SetAddress(addr); #ifndef WIN32 - if (!g_pGameConf->GetMemSig("Steam3Server", (void **)(&g_pSteam3ServerFunc)) || !g_pSteam3ServerFunc) - { - snprintf(error, maxlen, "Failed to find Steam3Server function.\n"); - return false; - } + if (!g_pGameConf->GetMemSig("Steam3Server", (void **)(&g_pSteam3ServerFunc)) || !g_pSteam3ServerFunc) + { + snprintf(error, maxlen, "Failed to find Steam3Server function.\n"); + return false; + } #else - void *address; - if (!g_pGameConf->GetMemSig("CBaseServer__CheckMasterServerRequestRestart", &address) || !address) - { - snprintf(error, maxlen, "Failed to find CBaseServer__CheckMasterServerRequestRestart function.\n"); - return false; - } + void *address; + if (!g_pGameConf->GetMemSig("CBaseServer__CheckMasterServerRequestRestart", &address) || !address) + { + snprintf(error, maxlen, "Failed to find CBaseServer__CheckMasterServerRequestRestart function.\n"); + return false; + } - int steam3ServerFuncOffset = 0; - if (!g_pGameConf->GetOffset("CheckMasterServerRequestRestart_Steam3ServerFuncOffset", &steam3ServerFuncOffset) || steam3ServerFuncOffset == 0) - { - snprintf(error, maxlen, "Failed to find CheckMasterServerRequestRestart_Steam3ServerFuncOffset offset.\n"); - return false; - } + int steam3ServerFuncOffset = 0; + if (!g_pGameConf->GetOffset("CheckMasterServerRequestRestart_Steam3ServerFuncOffset", &steam3ServerFuncOffset) || steam3ServerFuncOffset == 0) + { + snprintf(error, maxlen, "Failed to find CheckMasterServerRequestRestart_Steam3ServerFuncOffset offset.\n"); + return false; + } - //META_CONPRINTF("CheckMasterServerRequestRestart: %p\n", address); - address = (void *)((intptr_t)address + steam3ServerFuncOffset); - g_pSteam3ServerFunc = (Steam3ServerFunc)((intptr_t)address + *((int32_t *)address) + sizeof(int32_t)); - //META_CONPRINTF("Steam3Server: %p\n", g_pSteam3ServerFunc); + //META_CONPRINTF("CheckMasterServerRequestRestart: %p\n", address); + address = (void *)((intptr_t)address + steam3ServerFuncOffset); + g_pSteam3ServerFunc = (Steam3ServerFunc)((intptr_t)address + *((int32_t *)address) + sizeof(int32_t)); + //META_CONPRINTF("Steam3Server: %p\n", g_pSteam3ServerFunc); #endif - g_pSteam3Server = Steam3Server(); - if (!g_pSteam3Server) - { - snprintf(error, maxlen, "Unable to get Steam3Server singleton.\n"); - return false; - } + g_pSteam3Server = Steam3Server(); + if (!g_pSteam3Server) + { + snprintf(error, maxlen, "Unable to get Steam3Server singleton.\n"); + return false; + } - if (!g_pSteam3Server->m_pSteamGameServer) - { - snprintf(error, maxlen, "Unable to get Steam Game Server.\n"); - return false; - } + if (!g_pSteam3Server->m_pSteamGameServer) + { + snprintf(error, maxlen, "Unable to get Steam Game Server.\n"); + return false; + } - /* - META_CONPRINTF("ISteamGameServer: %p\n", g_pSteam3Server->m_pSteamGameServer); - META_CONPRINTF("ISteamUtils: %p\n", g_pSteam3Server->m_pSteamGameServerUtils); - META_CONPRINTF("ISteamNetworking: %p\n", g_pSteam3Server->m_pSteamGameServerNetworking); - META_CONPRINTF("ISteamGameServerStats: %p\n", g_pSteam3Server->m_pSteamGameServerStats); - */ - void** vtable = *((void***)g_pSteam3Server->m_pSteamGameServer); + void** vtable = *((void***)g_pSteam3Server->m_pSteamGameServer); - int offset = 0; - if (!g_pGameConf->GetOffset("ISteamGameServer__BeginAuthSession", &offset) || offset == 0) - { - snprintf(error, maxlen, "Failed to find ISteamGameServer__BeginAuthSession offset.\n"); - return false; - } - g_pBeginAuthSession.SetAddress(vtable[offset]); + if (!g_pGameConf->GetOffset("ISteamGameServer__BeginAuthSession", &g_nBeginAuthSessionOffset) || g_nBeginAuthSessionOffset == 0) + { + snprintf(error, maxlen, "Failed to find ISteamGameServer__BeginAuthSession offset.\n"); + return false; + } + g_pBeginAuthSession.SetAddress(vtable[g_nBeginAuthSessionOffset]); - SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, offset, 0, 0); - if (SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true) == 0) - { - snprintf(error, maxlen, "Failed to setup ISteamGameServer__BeginAuthSession hook.\n"); - return false; - } + SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, g_nBeginAuthSessionOffset, 0, 0); + if (SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true) == 0) + { + snprintf(error, maxlen, "Failed to setup ISteamGameServer__BeginAuthSession hook.\n"); + return false; + } + g_pLastHookedSteamGameServer = g_pSteam3Server->m_pSteamGameServer; - offset = 0; - if (!g_pGameConf->GetOffset("ISteamGameServer__EndAuthSession", &offset) || offset == 0) - { - snprintf(error, maxlen, "Failed to find ISteamGameServer__EndAuthSession offset.\n"); - return false; - } - g_pEndAuthSession.SetAddress(vtable[offset]); + int offset = 0; + if (!g_pGameConf->GetOffset("ISteamGameServer__EndAuthSession", &offset) || offset == 0) + { + snprintf(error, maxlen, "Failed to find ISteamGameServer__EndAuthSession offset.\n"); + return false; + } + g_pEndAuthSession.SetAddress(vtable[offset]); - CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); + CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); - detourCBaseServer__ConnectClient = DETOUR_CREATE_MEMBER(CBaseServer__ConnectClient, "CBaseServer__ConnectClient"); - if (detourCBaseServer__ConnectClient == nullptr) - { - snprintf(error, maxlen, "Failed to create CBaseServer__ConnectClient detour.\n"); - return false; - } - detourCBaseServer__ConnectClient->EnableDetour(); + detourCBaseServer__ConnectClient = DETOUR_CREATE_MEMBER(CBaseServer__ConnectClient, "CBaseServer__ConnectClient"); + if (detourCBaseServer__ConnectClient == nullptr) + { + snprintf(error, maxlen, "Failed to create CBaseServer__ConnectClient detour.\n"); + return false; + } + detourCBaseServer__ConnectClient->EnableDetour(); - g_pConnectForward = g_pForwards->CreateForward("OnClientPreConnectEx", ET_LowEvent, 5, NULL, Param_String, Param_String, Param_String, Param_String, Param_String); + g_Detour_CSteam3Server__OnValidateAuthTicketResponse = DETOUR_CREATE_MEMBER(CSteam3Server__OnValidateAuthTicketResponse, "CSteam3Server__OnValidateAuthTicketResponse"); + if(!g_Detour_CSteam3Server__OnValidateAuthTicketResponse) + { + snprintf(error, maxlen, "Failed to detour CSteam3Server__OnValidateAuthTicketResponse.\n"); + return false; + } + g_Detour_CSteam3Server__OnValidateAuthTicketResponse->EnableDetour(); - return true; + g_pConnectForward = g_pForwards->CreateForward("OnClientPreConnectEx", ET_LowEvent, 5, NULL, Param_String, Param_String, Param_String, Param_String, Param_String); + + return true; } bool Connect::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) { - GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); - ConVar_Register(0, this); + ConVar_Register(0, this); - return true; + return true; } void Connect::SDK_OnUnload() { - g_pForwards->ReleaseForward(g_pConnectForward); + g_pForwards->ReleaseForward(g_pConnectForward); - gameconfs->CloseGameConfigFile(g_pGameConf); + gameconfs->CloseGameConfigFile(g_pGameConf); } bool Connect::SDK_OnMetamodUnload(char *error, size_t maxlen) { - if (detourCBaseServer__ConnectClient) - { - detourCBaseServer__ConnectClient->DisableDetour(); - delete detourCBaseServer__ConnectClient; - } + if (detourCBaseServer__ConnectClient) + { + detourCBaseServer__ConnectClient->DisableDetour(); + delete detourCBaseServer__ConnectClient; + } + if (g_Detour_CSteam3Server__OnValidateAuthTicketResponse) + { + g_Detour_CSteam3Server__OnValidateAuthTicketResponse->DisableDetour(); + delete g_Detour_CSteam3Server__OnValidateAuthTicketResponse; + } - return true; + return true; } bool Connect::RegisterConCommandBase(ConCommandBase *pCommand) { - META_REGCVAR(pCommand); + META_REGCVAR(pCommand); - return true; + return true; +} + +cell_t SteamClientAuthenticated(IPluginContext *pContext, const cell_t *params) +{ + char *pSteamID; + pContext->LocalToString(params[1], &pSteamID); + + ConnectClientStorage Storage; + if(g_ConnectClientStorage.retrieve(pSteamID, &Storage)) + { + if (g_SvLogging.GetInt()) + g_pSM->LogMessage(myself, "%s SteamClientAuthenticated: %d", pSteamID, Storage.SteamLegal); + + return Storage.SteamLegal; + } + if (g_SvLogging.GetInt()) + g_pSM->LogMessage(myself, "%s SteamClientAuthenticated: FALSE!", pSteamID); + + return false; +} + +cell_t ClientPreConnectEx(IPluginContext *pContext, const cell_t *params) +{ + char *pSteamID; + pContext->LocalToString(params[1], &pSteamID); + + int retVal = params[2]; + + char *rejectReason; + pContext->LocalToString(params[3], &rejectReason); + + ConnectClientStorage Storage; + if(!g_ConnectClientStorage.retrieve(pSteamID, &Storage)) + { + return 1; + } + + if(retVal == 0) + { + g_pSM->LogMessage(myself, "inside ClientPreConnectEx return 0. steamID: %s", pSteamID); + g_pRejectConnectionFunc((CBaseServer*)Storage.pThis, Storage.address, Storage.iClientChallenge, rejectReason); + return 0; + } + + auto *pClient = DETOUR_MEMBER_MCALL_ORIGINAL(CBaseServer__ConnectClient, Storage.pThis)(Storage.address, Storage.nProtocol, Storage.iChallenge, Storage.iClientChallenge, Storage.nAuthProtocol, Storage.pchName, Storage.pchPassword, Storage.pCookie, Storage.cbCookie); + + if(!pClient) + { + return 1; + } + + bool force = g_SvNoSteam.GetInt() || g_SvForceSteam.GetInt(); + + if(Storage.SteamAuthFailed && force && !Storage.GotValidateAuthTicketResponse) + { + if (g_SvLogging.GetInt()) + g_pSM->LogMessage(myself, "%s Force ValidateAuthTicketResponse", pSteamID); + + Storage.ValidateAuthTicketResponse.m_SteamID = CSteamID(Storage.ullSteamID); + Storage.ValidateAuthTicketResponse.m_eAuthSessionResponse = k_EAuthSessionResponseOK; + Storage.ValidateAuthTicketResponse.m_OwnerSteamID = Storage.ValidateAuthTicketResponse.m_SteamID; + Storage.GotValidateAuthTicketResponse = true; + } + + // Make sure this is always called in order to verify the client on the server + if(Storage.GotValidateAuthTicketResponse) + { + if (g_SvLogging.GetInt()) + g_pSM->LogMessage(myself, "%s Replay ValidateAuthTicketResponse", pSteamID); + + DETOUR_MEMBER_MCALL_ORIGINAL(CSteam3Server__OnValidateAuthTicketResponse, g_pSteam3Server)(&Storage.ValidateAuthTicketResponse); + } + return 0; +} + +const sp_nativeinfo_t MyNatives[] = +{ + { "ClientPreConnectEx", ClientPreConnectEx }, + { "SteamClientAuthenticated", SteamClientAuthenticated }, + { NULL, NULL } +}; + +void Connect::SDK_OnAllLoaded() +{ + sharesys->AddNatives(myself, MyNatives); } diff --git a/extension/extension.h b/extension/extension.h index e1d9466..dd0280a 100644 --- a/extension/extension.h +++ b/extension/extension.h @@ -55,6 +55,13 @@ public: */ //virtual void SDK_OnPauseChange(bool paused); + /** + * @brief This is called once all known extensions have been loaded. + * Note: It is is a good idea to add natives here, if any are provided. + */ + virtual void SDK_OnAllLoaded(); + + /** * @brief this is called when Core wants to know if your extension is working. * @@ -110,6 +117,11 @@ public: \ ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \ ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name) +#define DETOUR_MEMBER_MCALL_CALLBACK(name, classptr) \ + ((name##Class *)classptr->*(&name##Class::name)) +#define DETOUR_MEMBER_MCALL_ORIGINAL(name, classptr) \ + ((name##Class *)classptr->*(name##Class::name##_Actual)) + struct mfpDetails { void *addr; intptr_t adjustor; diff --git a/extension/smsdk_config.h b/extension/smsdk_config.h index 6951814..2bc487d 100644 --- a/extension/smsdk_config.h +++ b/extension/smsdk_config.h @@ -40,8 +40,8 @@ #define SMEXT_CONF_NAME "Connect" #define SMEXT_CONF_DESCRIPTION "Forward for early connection" #define SMEXT_CONF_VERSION SM_FULL_VERSION -#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker" -#define SMEXT_CONF_URL "http://limetech.org/" +#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker + jenz + zacade" +#define SMEXT_CONF_URL "https://git.unloze.com/UNLOZE/sm-ext-connect" #define SMEXT_CONF_LOGTAG "CONNECT" #define SMEXT_CONF_LICENSE "GPL" #define SMEXT_CONF_DATESTRING __DATE__