diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 587db7e5..c9600edf 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -1914,7 +1914,7 @@ CPlayer::CPlayer() m_bIsSourceTV = false; m_bIsReplay = false; m_Serial.value = -1; - m_SteamAccountID = 0; + m_SteamId = k_steamIDNil; #if SOURCE_ENGINE == SE_CSGO m_LanguageCookie = InvalidQueryCvarCookie; #endif @@ -1997,7 +1997,7 @@ void CPlayer::Disconnect() m_bIsSourceTV = false; m_bIsReplay = false; m_Serial.value = -1; - m_SteamAccountID = 0; + m_SteamId = k_steamIDNil; #if SOURCE_ENGINE == SE_CSGO m_LanguageCookie = InvalidQueryCvarCookie; #endif @@ -2033,16 +2033,17 @@ const char *CPlayer::GetAuthString(bool validated) return m_AuthID.c_str(); } -unsigned int CPlayer::GetSteamAccountID(bool validated) +const CSteamID &CPlayer::GetSteamId(bool validated) { if (IsFakeClient() || (validated && !IsAuthStringValidated())) { - return 0; + static const CSteamID invalidId = k_steamIDNil; + return invalidId; } - if (m_SteamAccountID != 0) + if (m_SteamId.IsValid()) { - return m_SteamAccountID; + return m_SteamId; } #if SOURCE_ENGINE < SE_ORANGEBOX @@ -2050,22 +2051,35 @@ unsigned int CPlayer::GetSteamAccountID(bool validated) /* STEAM_0:1:123123 | STEAM_ID_LAN | STEAM_ID_PENDING */ if (pAuth && (strlen(pAuth) > 10) && pAuth[8] != '_') { - m_SteamAccountID = (atoi(&pAuth[8]) | (atoi(&pAuth[10]) << 1)); + m_SteamId = CSteamID(atoi(&pAuth[8]) | (atoi(&pAuth[10]) << 1), + k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeIndividual); } #else - unsigned long long *steamId; + const CSteamID *steamId; #if SOURCE_ENGINE == SE_DOTA - steamId = (unsigned long long *)engine->GetClientSteamID(m_iIndex); + steamId = engine->GetClientSteamID(m_iIndex); #else - steamId = (unsigned long long *)engine->GetClientSteamID(m_pEdict); + steamId = engine->GetClientSteamID(m_pEdict); #endif if (steamId) { - m_SteamAccountID = (*steamId & 0xFFFFFFFF); + m_SteamId = (*steamId); } #endif - return m_SteamAccountID; + return m_SteamId; +} + +unsigned int CPlayer::GetSteamAccountID(bool validated) +{ + if (!IsFakeClient() && (!validated || IsAuthStringValidated())) + { + const CSteamID &id = GetSteamId(); + if (id.IsValid()) + return id.GetAccountID(); + } + + return 0; } edict_t *CPlayer::GetEdict() diff --git a/core/PlayerManager.h b/core/PlayerManager.h index 9bb58f0b..cfd57cc0 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -43,6 +43,8 @@ #include #include "ConVarManager.h" +#include + using namespace SourceHook; #define PLAYER_LIFE_UNKNOWN 0 @@ -71,6 +73,8 @@ public: const char *GetIPAddress(); const char *GetAuthString(bool validated = true); unsigned int GetSteamAccountID(bool validated = true); + const CSteamID &GetSteamId(bool validated = true); + uint64_t GetSteamId64(bool validated = true) { return GetSteamId(validated).ConvertToUint64(); } edict_t *GetEdict(); bool IsInGame(); bool WasCountedAsInGame(); @@ -130,7 +134,7 @@ private: bool m_bIsSourceTV; bool m_bIsReplay; serial_t m_Serial; - unsigned int m_SteamAccountID; + CSteamID m_SteamId; #if SOURCE_ENGINE == SE_CSGO QueryCvarCookie_t m_LanguageCookie; #endif diff --git a/core/logic/smn_players.cpp b/core/logic/smn_players.cpp index 47288f0a..34b24798 100644 --- a/core/logic/smn_players.cpp +++ b/core/logic/smn_players.cpp @@ -37,12 +37,21 @@ #include #include #include +#include "GameConfigs.h" #include "CellArray.h" #include "AutoHandleRooter.h" using namespace SourceHook; using namespace SourceMod; +#ifndef PRIu64 +#ifdef _WIN32 +#define PRIu64 "I64u" +#else +#define PRIu64 "llu" +#endif +#endif + static const int kActivityNone = 0; static const int kActivityNonAdmins = 1; // Show admin activity to non-admins anonymously. static const int kActivityNonAdminsNames = 2; // If 1 is specified, admin names will be shown. @@ -322,9 +331,17 @@ static cell_t sm_GetClientIP(IPluginContext *pCtx, const cell_t *params) return 1; } -static cell_t sm_GetClientAuthStr(IPluginContext *pCtx, const cell_t *params) +// Must match clients.inc +enum class AuthIdType +{ + Engine = 0, + Steam2, + Steam3, + SteamId64, +}; + +static cell_t SteamIdToLocal(IPluginContext *pCtx, int index, AuthIdType authType, cell_t local_addr, size_t bytes, bool validate) { - int index = params[1]; if ((index < 1) || (index > playerhelpers->GetMaxClients())) { return pCtx->ThrowNativeError("Client index %d is invalid", index); @@ -336,24 +353,113 @@ static cell_t sm_GetClientAuthStr(IPluginContext *pCtx, const cell_t *params) return pCtx->ThrowNativeError("Client %d is not connected", index); } - bool validate = true; - if (params[0] > 3) - { - validate = !!params[4]; - } - - const char *authstr = pPlayer->GetAuthString(validate); - - if (!authstr || authstr[0] == '\0') + switch (authType) { - return 0; - } + case AuthIdType::Engine: + { + const char *authstr = pPlayer->GetAuthString(validate); + if (!authstr || authstr[0] == '\0') + { + return 0; + } - pCtx->StringToLocal(params[2], static_cast(params[3]), authstr); + pCtx->StringToLocal(local_addr, bytes, authstr); + } + break; + case AuthIdType::Steam2: + case AuthIdType::Steam3: + { + if (pPlayer->IsFakeClient()) + { + pCtx->StringToLocal(local_addr, bytes, "BOT"); + return 1; + } + + uint64_t steamId = pPlayer->GetSteamId64(validate); + if (steamId == 0) + { + if (gamehelpers->IsLANServer()) + { + pCtx->StringToLocal(local_addr, bytes, "STEAM_ID_LAN"); + return 1; + } + else if (!validate) + { + pCtx->StringToLocal(local_addr, bytes, "STEAM_ID_PENDING"); + return 1; + } + else + { + return 0; + } + } + + char szAuth[64]; + unsigned int universe = steamId >> 56; + unsigned int accountId = steamId & 0xFFFFFFFF; + unsigned int instance = (steamId >> 32) & 0x000FFFFF; + if (authType == AuthIdType::Steam2) + { + if (atoi(g_pGameConf->GetKeyValue("UseInvalidUniverseInSteam2IDs")) == 1) + { + universe = 0; + } + + snprintf(szAuth, sizeof(szAuth), "STEAM_%u:%u:%u", universe, accountId & 1, accountId >> 1); + } + else if (instance != 1) + { + snprintf(szAuth, sizeof(szAuth), "[U:%u:%u:%u]", universe, accountId, instance); + } + else + { + snprintf(szAuth, sizeof(szAuth), "[U:%u:%u]", universe, accountId); + } + + pCtx->StringToLocal(local_addr, bytes, szAuth); + } + break; + + case AuthIdType::SteamId64: + { + if (pPlayer->IsFakeClient() || gamehelpers->IsLANServer()) + { + return 0; + } + + uint64_t steamId = pPlayer->GetSteamId64(validate); + if (steamId == 0) + { + return 0; + } + + char szAuth[64]; + snprintf(szAuth, sizeof(szAuth), "%" PRIu64, steamId); + + pCtx->StringToLocal(local_addr, bytes, szAuth); + } + break; + } return 1; } +static cell_t sm_GetClientAuthStr(IPluginContext *pCtx, const cell_t *params) +{ + bool validate = true; + if (params[0] >= 4) + { + validate = !!params[4]; + } + + return SteamIdToLocal(pCtx, params[1], AuthIdType::Steam2, params[2], (size_t)params[3], validate); +} + +static cell_t sm_GetClientAuthId(IPluginContext *pCtx, const cell_t *params) +{ + return SteamIdToLocal(pCtx, params[1], (AuthIdType)params[2], params[3], (size_t)params[4], params[5] != 0); +} + static cell_t sm_GetSteamAccountID(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; @@ -1540,6 +1646,7 @@ REGISTER_NATIVES(playernatives) { "CanUserTarget", CanUserTarget }, { "ChangeClientTeam", ChangeClientTeam }, { "GetClientAuthString", sm_GetClientAuthStr }, + { "GetClientAuthId", sm_GetClientAuthId }, { "GetSteamAccountID", sm_GetSteamAccountID }, { "GetClientCount", sm_GetClientCount }, { "GetClientInfo", sm_GetClientInfo }, diff --git a/gamedata/core.games/engine.bgt.txt b/gamedata/core.games/engine.bgt.txt index 674993e8..c4502dbb 100644 --- a/gamedata/core.games/engine.bgt.txt +++ b/gamedata/core.games/engine.bgt.txt @@ -36,5 +36,10 @@ "windows" "\xE8\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xB9\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x6A" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.css.txt b/gamedata/core.games/engine.css.txt index cf08b2b0..2ec1cc75 100644 --- a/gamedata/core.games/engine.css.txt +++ b/gamedata/core.games/engine.css.txt @@ -53,5 +53,10 @@ "mac" "@gEntList" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.darkm.txt b/gamedata/core.games/engine.darkm.txt index d84d54b3..f458d257 100644 --- a/gamedata/core.games/engine.darkm.txt +++ b/gamedata/core.games/engine.darkm.txt @@ -42,5 +42,10 @@ "windows" "\xE8\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xB9\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xE8" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.ep1.txt b/gamedata/core.games/engine.ep1.txt index de669556..932f92ca 100644 --- a/gamedata/core.games/engine.ep1.txt +++ b/gamedata/core.games/engine.ep1.txt @@ -106,6 +106,11 @@ "windows" "\x56\x8B\x74\x24\x08\x57\x56\x8B\xF9\xE8\x2A\x2A\x2A\x2A\x84\xC0\x0F\x85\xC4\x00\x00\x00\x56\x8D" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.ep2.txt b/gamedata/core.games/engine.ep2.txt index ba0a3460..d1a4f5bd 100644 --- a/gamedata/core.games/engine.ep2.txt +++ b/gamedata/core.games/engine.ep2.txt @@ -58,4 +58,12 @@ } } } + + "#default" + { + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } + } } diff --git a/gamedata/core.games/engine.ep2valve.txt b/gamedata/core.games/engine.ep2valve.txt index 55eef306..0dc63e82 100644 --- a/gamedata/core.games/engine.ep2valve.txt +++ b/gamedata/core.games/engine.ep2valve.txt @@ -52,5 +52,10 @@ "mac" "@gEntList" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.eye.txt b/gamedata/core.games/engine.eye.txt index f99e691d..045f65aa 100644 --- a/gamedata/core.games/engine.eye.txt +++ b/gamedata/core.games/engine.eye.txt @@ -41,5 +41,10 @@ "windows" "\xE8\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xB9\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xE8" } } + + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } } } diff --git a/gamedata/core.games/engine.sdk2013.txt b/gamedata/core.games/engine.sdk2013.txt index 498fb367..4912944c 100644 --- a/gamedata/core.games/engine.sdk2013.txt +++ b/gamedata/core.games/engine.sdk2013.txt @@ -95,4 +95,12 @@ } } } + + "#default" + { + "Keys" + { + "UseInvalidUniverseInSteam2IDs" "1" + } + } } diff --git a/plugins/include/clients.inc b/plugins/include/clients.inc index 174ca0ad..b30a3040 100644 --- a/plugins/include/clients.inc +++ b/plugins/include/clients.inc @@ -45,6 +45,25 @@ enum NetFlow NetFlow_Both, /**< Both values added together */ }; +/** + * Auth string types. + * + * Note that for the Steam2 and Steam3 types, the following ids are + * also valid values: + * "STEAM_ID_PENDING" - Authentication is pending. + * "STEAM_ID_LAN" - Authentication is disabled because of being on a LAN server. + * "BOT" - The client is a bot. + */ +enum AuthIdType +{ + AuthId_Engine = 0, /**< The game-specific auth string as returned from the engine */ + + // The following are only available on games that support Steam authentication. + AuthId_Steam2, /**< Steam2 rendered format, ex "STEAM_1:1:4153990" */ + AuthId_Steam3, /**< Steam3 rendered format, ex "[U:1:8307981]" */ + AuthId_SteamID64, /**< A SteamID64 (uint64) as a String, ex "76561197968573709" */ +}; + /** * MAXPLAYERS is not the same as MaxClients. * MAXPLAYERS is a hardcoded value as an upper limit. MaxClients changes based on the server. @@ -267,8 +286,24 @@ native bool:GetClientIP(client, String:ip[], maxlen, bool:remport=true); * @return True on success, false otherwise. * @error If the client is not connected or the index is invalid. */ +#pragma deprecated Use GetClientAuthId native bool:GetClientAuthString(client, String:auth[], maxlen, bool:validate=true); +/** + * Retrieves a client's authentication string (SteamID). + * + * @param client Player index. + * @param authType Auth id type and format to use. + * @param auth Buffer to store the client's auth id. + * @param maxlen Maximum length of string buffer (includes NULL terminator). + * @param validate Check backend validation status. + * DO NOT PASS FALSE UNLESS YOU UNDERSTAND THE CONSEQUENCES, + * You WILL KNOW if you need to use this, MOST WILL NOT. + * @return True on success, false otherwise. + * @error If the client is not connected or the index is invalid. + */ +native bool:GetClientAuthId(client, AuthIdType:authType, String:auth[], maxlen, bool:validate=true); + /** * Returns the client's Steam account ID. * diff --git a/public/IPlayerHelpers.h b/public/IPlayerHelpers.h index 8b1d9cd2..35a174a7 100644 --- a/public/IPlayerHelpers.h +++ b/public/IPlayerHelpers.h @@ -41,7 +41,7 @@ #include #define SMINTERFACE_PLAYERMANAGER_NAME "IPlayerManager" -#define SMINTERFACE_PLAYERMANAGER_VERSION 20 +#define SMINTERFACE_PLAYERMANAGER_VERSION 21 struct edict_t; class IPlayerInfo; @@ -267,6 +267,15 @@ namespace SourceMod * @brief Removes admin access from the client. */ virtual void ClearAdmin() =0; + + /** + * @brief Returns the client's Steam ID as a uint64. + * + * @param validated Check backend validation status. + * + * @return Steam Id or 0 if not available. + */ + virtual uint64_t GetSteamId64(bool validated = true) =0; }; /**