Improve client-side online authentication error handling and UX

Display the error to the user without having to open the developer console or terminal window. This patch also adds printing to COM_ExplainDisconnection (which has been stripped out of the retail binary).
This commit is contained in:
Kawe Mazidjatari 2023-10-22 17:00:56 +02:00
parent 1dea3575ef
commit c6f2c99619
9 changed files with 169 additions and 21 deletions

View File

@ -206,6 +206,7 @@ ConVar* cl_threaded_bone_setup = nullptr;
ConVar* cl_language = nullptr;
ConVar* cl_onlineAuthEnable = nullptr;
ConVar* cl_onlineAuthToken = nullptr;
ConVar* cl_onlineAuthTokenSignature1 = nullptr;
ConVar* cl_onlineAuthTokenSignature2 = nullptr;
@ -387,7 +388,7 @@ void ConVar_StaticInit(void)
sv_minPersonaNameLength = ConVar::StaticCreate("sv_minPersonaNameLength", "4" , FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr);
sv_maxPersonaNameLength = ConVar::StaticCreate("sv_maxPersonaNameLength", "16", FCVAR_RELEASE, "The maximum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr);
sv_onlineAuthEnable = ConVar::StaticCreate("sv_onlineAuthEnable" , "1" , FCVAR_RELEASE, "Enables the online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr);
sv_onlineAuthEnable = ConVar::StaticCreate("sv_onlineAuthEnable" , "1" , FCVAR_RELEASE, "Enables the server-side online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr);
sv_onlineAuthValidateExpiry = ConVar::StaticCreate("sv_onlineAuthValidateExpiry" , "1" , FCVAR_RELEASE, "Validate the online authentication token 'expiry' claim.", true, 0.f, true, 1.f, nullptr, nullptr);
sv_onlineAuthExpiryTolerance = ConVar::StaticCreate("sv_onlineAuthExpiryTolerance" , "1" , FCVAR_RELEASE, "The online authentication token 'expiry' claim tolerance in seconds.", true, 0.f, true, float(UINT8_MAX), nullptr, "Must range between [0,255]");
sv_onlineAuthValidateIssuedAt = ConVar::StaticCreate("sv_onlineAuthValidateIssuedAt" , "1" , FCVAR_RELEASE, "Validate the online authentication token 'issued at' claim.", true, 0.f, true, 1.f, nullptr, nullptr);
@ -425,7 +426,8 @@ void ConVar_StaticInit(void)
cl_materialinfo_offset_x = ConVar::StaticCreate("cl_materialinfo_offset_x", "0" , FCVAR_DEVELOPMENTONLY, "X offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr);
cl_materialinfo_offset_y = ConVar::StaticCreate("cl_materialinfo_offset_y", "420", FCVAR_DEVELOPMENTONLY, "Y offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr);
cl_onlineAuthToken = ConVar::StaticCreate("cl_onlineAuthToken", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr);
cl_onlineAuthEnable = ConVar::StaticCreate("cl_onlineAuthEnable" ,"1", FCVAR_RELEASE, "Enables the client-side online authentication system.", true, 0.f, true, 1.f, nullptr, nullptr);
cl_onlineAuthToken = ConVar::StaticCreate("cl_onlineAuthToken" , "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr);
cl_onlineAuthTokenSignature1 = ConVar::StaticCreate("cl_onlineAuthTokenSignature1", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr);
cl_onlineAuthTokenSignature2 = ConVar::StaticCreate("cl_onlineAuthTokenSignature2", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "", false, 0.f, false, 0.f, nullptr, nullptr);

View File

@ -197,6 +197,7 @@ extern ConVar* cl_threaded_bone_setup;
extern ConVar* cl_language;
extern ConVar* cl_onlineAuthEnable;
extern ConVar* cl_onlineAuthToken;
extern ConVar* cl_onlineAuthTokenSignature1;
extern ConVar* cl_onlineAuthTokenSignature2;

View File

@ -11,6 +11,7 @@
#include "core/stdafx.h"
#include "vpc/keyvalues.h"
#include "tier0/frametask.h"
#include "engine/common.h"
#include "engine/host.h"
#include "clientstate.h"
#include "common/callback.h"
@ -167,10 +168,14 @@ bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick
//------------------------------------------------------------------------------
// Purpose: get authentication token for current connection context
// Input : *connectParams -
// *reasonBuf -
// reasonBufLen -
// Output : true on success, false otherwise
//------------------------------------------------------------------------------
bool CClientState::Authenticate(connectparams_t* connectParams) const
bool CClientState::Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const
{
#define FORMAT_ERROR_REASON(fmt, ...) V_snprintf(reasonBuf, reasonBufLen, fmt, ##__VA_ARGS__);
string msToken; // token returned by the masterserver authorising the client to play online
string message; // message returned by the masterserver about the result of the auth
@ -180,7 +185,7 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const
const bool ret = g_pMasterServer->AuthForConnection(*g_NucleusID, connectParams->netAdr, g_OriginAuthCode, msToken, message);
if (!ret)
{
Error(eDLL_T::ENGINE, NO_ERROR, "Failed to authenticate for online play: %s\n", message.c_str());
FORMAT_ERROR_REASON("%s", message.c_str());
return false;
}
@ -192,7 +197,7 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const
if (!tokenSignatureDelim)
{
Error(eDLL_T::ENGINE, NO_ERROR, "Failed to authenticate for online play: %s\n", "Invalid token returned by MS");
FORMAT_ERROR_REASON("Invalid token returned by MS");
return false;
}
@ -219,12 +224,26 @@ bool CClientState::Authenticate(connectparams_t* connectParams) const
}
return true;
#undef REJECT_CONNECTION
}
bool IsLocalHost(connectparams_t* connectParams)
{
return (strstr(connectParams->netAdr, "localhost") || strstr(connectParams->netAdr, "127.0.0.1"));
}
void CClientState::VConnect(CClientState* thisptr, connectparams_t* connectParams)
{
if (strncmp(connectParams->netAdr, "localhost", 9) != NULL)
thisptr->Authenticate(connectParams);
if (cl_onlineAuthEnable->GetBool() && !IsLocalHost(connectParams))
{
char authFailReason[512];
if (!thisptr->Authenticate(connectParams, authFailReason, sizeof(authFailReason)))
{
COM_ExplainDisconnection(true, "Failed to authenticate for online play: %s", authFailReason);
return;
}
}
CClientState__Connect(thisptr, connectParams);
}

View File

@ -57,7 +57,7 @@ public:
float GetFrameTime(void) const;
bool Authenticate(connectparams_t* connectParams) const;
bool Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const;
int m_Socket;
int _padding_maybe;

View File

@ -6,6 +6,7 @@
#include <core/stdafx.h>
#include <tier1/strtools.h>
#include <localize/localize.h>
#include <engine/common.h>
/*
@ -42,4 +43,59 @@ const char* COM_FormatSeconds(int seconds)
}
return string;
}
/*
==============================
COM_ExplainDisconnection
==============================
*/
void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...)
{
char szBuf[1024];
{/////////////////////////////
va_list vArgs;
va_start(vArgs, fmt);
vsnprintf(szBuf, sizeof(szBuf), fmt, vArgs);
szBuf[sizeof(szBuf) - 1] = '\0';
va_end(vArgs);
}/////////////////////////////
if (bPrint)
{
if (szBuf[0] == '#')
{
wchar_t formatStr[1024];
const wchar_t* wpchReason = (*g_ppVGuiLocalize) ? (*g_ppVGuiLocalize)->Find(szBuf) : nullptr;
if (wpchReason)
{
wcsncpy(formatStr, wpchReason, sizeof(formatStr) / sizeof(wchar_t));
char conStr[256];
(*g_ppVGuiLocalize)->ConvertUnicodeToANSI(formatStr, conStr, sizeof(conStr));
Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", conStr);
}
else
Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", szBuf);
}
else
{
Error(eDLL_T::ENGINE, NO_ERROR, "%s\n", szBuf);
}
}
v_COM_ExplainDisconnection(bPrint, szBuf);
}
void VCommon::Attach() const
{
DetourAttach(&v_COM_ExplainDisconnection, COM_ExplainDisconnection);
}
void VCommon::Detach() const
{
DetourDetach(&v_COM_ExplainDisconnection, COM_ExplainDisconnection);
}

View File

@ -2,31 +2,38 @@
/* ==== COMMON ========================================================================================================================================================== */
inline CMemory p_COM_InitFilesystem;
inline void*(*COM_InitFilesystem)(const char* pFullModPath);
inline void*(*v_COM_InitFilesystem)(const char* pFullModPath);
inline CMemory p_COM_GetPrintMessageBuffer;
inline char* const(*v_COM_GetPrintMessageBuffer)(void);
inline CMemory p_COM_ExplainDisconnection;
inline void*(*COM_ExplainDisconnection)(uint64_t level, const char* fmt, ...);
inline void(*v_COM_ExplainDisconnection)(bool bPrint, const char* fmt, ...);
const char* COM_FormatSeconds(int seconds);
void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...);
///////////////////////////////////////////////////////////////////////////////
class VCommon : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("COM_InitFilesystem", p_COM_InitFilesystem.GetPtr());
LogFunAdr("COM_GetPrintMessageBuffer", p_COM_GetPrintMessageBuffer.GetPtr());
LogFunAdr("COM_ExplainDisconnection", p_COM_ExplainDisconnection.GetPtr());
}
virtual void GetFun(void) const
{
p_COM_InitFilesystem = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??");
p_COM_GetPrintMessageBuffer = g_GameDll.FindPatternSIMD("48 8D 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 4C 89 44 24 ??");
p_COM_ExplainDisconnection = g_GameDll.FindPatternSIMD("48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??");
COM_InitFilesystem = p_COM_InitFilesystem.RCast<void* (*)(const char*)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??*/
COM_ExplainDisconnection = p_COM_ExplainDisconnection.RCast<void* (*)(uint64_t, const char*, ...)>(); /*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??*/
v_COM_InitFilesystem = p_COM_InitFilesystem.RCast<void* (*)(const char*)>();
v_COM_GetPrintMessageBuffer = p_COM_GetPrintMessageBuffer.RCast<char* const(*)(void)>();
v_COM_ExplainDisconnection = p_COM_ExplainDisconnection.RCast<void(*)(bool, const char*, ...)>();
}
virtual void GetVar(void) const { }
virtual void GetCon(void) const { }
virtual void Attach(void) const { }
virtual void Detach(void) const { }
virtual void Attach(void) const;
virtual void Detach(void) const;
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -147,7 +147,7 @@ void CEngineAPI::VSetStartupInfo(CEngineAPI* pEngineAPI, StartupInfo_t* pStartup
InitVPKSystem();
v_TRACEINIT(NULL, "COM_InitFilesystem( m_StartupInfo.m_szInitialMod )", "COM_ShutdownFileSystem()");
COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod);
v_COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod);
*g_bTextMode = true;
#else

View File

@ -3,9 +3,14 @@
bool Localize_IsLanguageSupported(const char* pLocaleName);
class CLocalize
class CLocalize : public CBaseAppSystem< ILocalize >
{
// todo
char unk[400];
unsigned __int16 m_CurrentFile;
char unk_19A;
bool m_bUseOnlyLongestLanguageString;
bool m_bSuppressChangeCallbacks;
bool m_bQueuedChangeCallback;
};
inline CMemory p_CLocalize__AddFile;
@ -15,8 +20,8 @@ inline CMemory p_CLocalize__LoadLocalizationFileLists;
inline bool(*v_CLocalize__LoadLocalizationFileLists)(CLocalize * thisptr);
inline CLocalize* g_pVGuiLocalize;
inline CLocalize* g_pLocalize;
inline CLocalize** g_ppVGuiLocalize;
inline CLocalize** g_ppLocalize;
///////////////////////////////////////////////////////////////////////////////
class VLocalize : public IDetour
@ -25,6 +30,7 @@ class VLocalize : public IDetour
{
LogFunAdr("CLocalize::AddFile", p_CLocalize__AddFile.GetPtr());
LogFunAdr("CLocalize::LoadLocalizationFileLists", p_CLocalize__LoadLocalizationFileLists.GetPtr());
LogFunAdr("g_Localize", reinterpret_cast<uintptr_t>(g_ppLocalize));
}
virtual void GetFun(void) const
{
@ -36,8 +42,8 @@ class VLocalize : public IDetour
}
virtual void GetVar(void) const
{
g_pVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast<CLocalize*>();
g_pLocalize = g_pVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create
g_ppVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast<CLocalize**>();
g_ppLocalize = g_ppVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create
}
virtual void GetCon(void) const { }
virtual void Attach(void) const;

View File

@ -1,6 +1,63 @@
#ifndef LOCALIZE_H
#define LOCALIZE_H
// direct references to localized strings
typedef unsigned long StringIndex_t;
const unsigned long INVALID_LOCALIZE_STRING_INDEX = (StringIndex_t)-1;
abstract_class ILocalize : public IAppSystem
{
public:
virtual bool LoadLocalizationFileLists() = 0;
// adds the contents of a file to the localization table
virtual bool AddFile(const char* fileName, const char* pPathID = NULL) = 0;
// Remove all strings from the table
virtual void RemoveAll() = 0;
// Finds the localized text for tokenName
virtual wchar_t* Find(char const* tokenName) = 0;
virtual void* FindIndex_Unknown(StringIndex_t index) = 0;
// converts an english string to unicode
// returns the number of wchar_t in resulting string, including null terminator
virtual int ConvertANSIToUnicode(const char* ansi, OUT_Z_BYTECAP(unicodeBufferSizeInBytes) wchar_t* unicode, ssize_t unicodeBufferSizeInBytes) = 0;
// converts an unicode string to an english string
// unrepresentable characters are converted to system default
// returns the number of characters in resulting string, including null terminator
virtual int ConvertUnicodeToANSI(const wchar_t* unicode, OUT_Z_BYTECAP(ansiBufferSize) char* ansi, ssize_t ansiBufferSize) = 0;
virtual StringIndex_t FindIndex(StringIndex_t index) = 0;
//!!! TODO !!!
//void* func_80[3];
//virtual void ConstructString(CLocalize*, char*, __int64, __int64, ...);
//__int64(__fastcall* GetNameByIndex)(CLocalize*, int);
//__int64(__fastcall* GetValueByIndex)(CLocalize*, int);
//__int64(__fastcall* GetFirstStringIndex)(CLocalize*);
//__int64(__fastcall* GetNextStringIndex)(CLocalize*, int);
//void* func_C0[6];
//__int64(__fastcall* ReloadLocalizationFiles)(CLocalize*);
//void* func_F8[6];
//__int64(__fastcall* ConvertANSIToUCS2)(CLocalize*, const char*, void*, __int64);
//void(__fastcall* ConvertUCS2ToANSI)(CLocalize*, __int16*, char*, int);
};
inline const char* const g_LanguageNames[] = {
"english",
"german",