mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
This patch splits host logic from CServerListManager. CServerListManager is actually meant for the client to manage the server list to which the client could connect to. The hosting logic has been moved to the new CServerHostManager class. Previously, we stored all the hosting details in CServerListManager, with connection criteria in CPylon, this data has been moved over to CServerHostManager as well. Previously, we also needed a mutex to access the server host data, function HostState_KeepAlive() has been refactored to the point this mutex is no longer necessary as the only threaded process is the actual request, the rest is being applied in the main thread. We also now only construct a NetGameServer_t struct if we actually plan to host. Access to CPylon::m_Language is now also protected by a mutex, as the change callback of cvar 'language' and the threaded method 'CPylon::QueryServer()' are competing for access.
600 lines
16 KiB
C++
600 lines
16 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: Callback functions for ConVar's.
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "core/stdafx.h"
|
|
#include "core/init.h"
|
|
#include "windows/id3dx.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "tier1/cvar.h"
|
|
#include "tier1/fmtstr.h"
|
|
#ifndef CLIENT_DLL
|
|
#include "engine/server/sv_rcon.h"
|
|
#endif // !CLIENT_DLL
|
|
#ifndef DEDICATED
|
|
#include "engine/client/cl_rcon.h"
|
|
#include "engine/client/cdll_engine_int.h"
|
|
#include "engine/client/clientstate.h"
|
|
#endif // !DEDICATED
|
|
#include "engine/client/client.h"
|
|
#include "engine/net.h"
|
|
#include "engine/host_cmd.h"
|
|
#include "engine/host_state.h"
|
|
#include "engine/enginetrace.h"
|
|
#ifndef CLIENT_DLL
|
|
#include "engine/server/server.h"
|
|
#endif // !CLIENT_DLL
|
|
|
|
#include "rtech/pak/pakencode.h"
|
|
#include "rtech/pak/pakdecode.h"
|
|
#include "rtech/pak/pakparse.h"
|
|
#include "rtech/pak/pakstate.h"
|
|
#include "rtech/pak/paktools.h"
|
|
|
|
#include "rtech/playlists/playlists.h"
|
|
|
|
#include "filesystem/basefilesystem.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "vpklib/packedstore.h"
|
|
#include "vscript/vscript.h"
|
|
#include "localize/localize.h"
|
|
#include "ebisusdk/EbisuSDK.h"
|
|
#ifndef DEDICATED
|
|
#include "geforce/reflex.h"
|
|
#include "gameui/IBrowser.h"
|
|
#include "gameui/IConsole.h"
|
|
#endif // !DEDICATED
|
|
#ifndef CLIENT_DLL
|
|
#include "networksystem/bansystem.h"
|
|
#endif // !CLIENT_DLL
|
|
#include "public/edict.h"
|
|
#include "public/worldsize.h"
|
|
#include "mathlib/crc32.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "common/completion.h"
|
|
#include "common/callback.h"
|
|
#ifndef DEDICATED
|
|
#include "materialsystem/cmaterialglue.h"
|
|
#endif // !DEDICATED
|
|
#include "public/bspflags.h"
|
|
#include "public/cmodel.h"
|
|
#include "public/idebugoverlay.h"
|
|
#include "public/localize/ilocalize.h"
|
|
#ifndef CLIENT_DLL
|
|
#include "game/server/detour_impl.h"
|
|
#include "game/server/gameinterface.h"
|
|
#endif // !CLIENT_DLL
|
|
#ifndef DEDICATED
|
|
#include "game/client/cliententitylist.h"
|
|
#include "game/client/viewrender.h"
|
|
#endif // !DEDICATED
|
|
|
|
|
|
/*
|
|
=====================
|
|
MP_GameMode_Changed_f
|
|
=====================
|
|
*/
|
|
void MP_GameMode_Changed_f(IConVar* pConVar, const char* pOldString)
|
|
{
|
|
v_SetupGamemode(mp_gamemode->GetString());
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
/*
|
|
=====================
|
|
Host_Changelevel_f
|
|
|
|
Goes to a new map,
|
|
taking all clients along
|
|
=====================
|
|
*/
|
|
void Host_Changelevel_f(const CCommand& args)
|
|
{
|
|
const int argCount = args.ArgC();
|
|
|
|
if (argCount >= 2
|
|
&& IsOriginInitialized()
|
|
&& g_pServer->IsActive())
|
|
{
|
|
const char* levelName = args[1];
|
|
const char* landMarkName = argCount > 2 ? args[2] : "";
|
|
|
|
v_SetLaunchOptions(args);
|
|
v_HostState_ChangeLevelMP(levelName, landMarkName);
|
|
}
|
|
}
|
|
#endif // !CLIENT_DLL
|
|
|
|
// TODO: move this to 'packedstore.cpp' and move everything in that file to 'packetstorebuilder.cpp'
|
|
static ConVar fs_packedstore_workspace("fs_packedstore_workspace", "ship", FCVAR_DEVELOPMENTONLY, "Determines the current VPK workspace.");
|
|
static ConVar fs_packedstore_compression_level("fs_packedstore_compression_level", "default", FCVAR_DEVELOPMENTONLY, "Determines the VPK compression level.", "fastest faster default better uber");
|
|
static ConVar fs_packedstore_max_helper_threads("fs_packedstore_max_helper_threads", "-1", FCVAR_DEVELOPMENTONLY, "Max # of additional \"helper\" threads to create during compression.", true, -1, true, LZHAM_MAX_HELPER_THREADS, "Must range between [-1,LZHAM_MAX_HELPER_THREADS], where -1=max practical");
|
|
|
|
/*
|
|
=====================
|
|
VPK_Pack_f
|
|
|
|
Packs VPK files into
|
|
'PLATFORM' VPK directory.
|
|
=====================
|
|
*/
|
|
void VPK_Pack_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() < 4)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const char* workspacePath = fs_packedstore_workspace.GetString();
|
|
|
|
if (!FileSystem()->IsDirectory(workspacePath, "PLATFORM"))
|
|
{
|
|
Error(eDLL_T::FS, NO_ERROR, "Workspace path \"%s\" doesn't exist!\n", workspacePath);
|
|
return;
|
|
}
|
|
|
|
VPKPair_t pair(args.Arg(1), args.Arg(2), args.Arg(3), NULL);
|
|
Msg(eDLL_T::FS, "*** Starting VPK build command for: '%s'\n", pair.m_DirName.String());
|
|
|
|
CFastTimer timer;
|
|
timer.Start();
|
|
|
|
CPackedStoreBuilder builder;
|
|
|
|
builder.InitLzEncoder(fs_packedstore_max_helper_threads.GetInt(), fs_packedstore_compression_level.GetString());
|
|
builder.PackStore(pair, workspacePath, "vpk/");
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds());
|
|
Msg(eDLL_T::FS, "\n");
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
VPK_Unpack_f
|
|
|
|
Unpacks VPK files into
|
|
workspace directory.
|
|
=====================
|
|
*/
|
|
void VPK_Unpack_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CUtlString fileName = args.Arg(1);
|
|
VPKDir_t vpk(fileName, (args.ArgC() > 2));
|
|
|
|
if (vpk.Failed())
|
|
{
|
|
Error(eDLL_T::FS, NO_ERROR, "Failed to parse directory tree file \"%s\"!\n", fileName.String());
|
|
return;
|
|
}
|
|
|
|
Msg(eDLL_T::FS, "*** Starting VPK extraction command for: '%s'\n", fileName.String());
|
|
|
|
CFastTimer timer;
|
|
timer.Start();
|
|
|
|
CPackedStoreBuilder builder;
|
|
|
|
builder.InitLzDecoder();
|
|
builder.UnpackStore(vpk, fs_packedstore_workspace.GetString());
|
|
|
|
timer.End();
|
|
Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds());
|
|
Msg(eDLL_T::FS, "\n");
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
VPK_Mount_f
|
|
|
|
Mounts input VPK file for
|
|
internal FileSystem usage
|
|
=====================
|
|
*/
|
|
void VPK_Mount_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FileSystem()->MountVPKFile(args.Arg(1));
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
VPK_Unmount_f
|
|
|
|
Unmounts input VPK file
|
|
and clears its cache
|
|
=====================
|
|
*/
|
|
void VPK_Unmount_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FileSystem()->UnmountVPKFile(args.Arg(1));
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
NET_UseSocketsForLoopbackChanged_f
|
|
|
|
Use random AES encryption
|
|
key for game packets
|
|
=====================
|
|
*/
|
|
void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString)
|
|
{
|
|
if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName()))
|
|
{
|
|
if (strcmp(pOldString, pConVarRef->GetString()) == NULL)
|
|
return; // Same value.
|
|
|
|
#ifndef CLIENT_DLL
|
|
// Reboot the RCON server to switch address type.
|
|
if (RCONServer()->IsInitialized())
|
|
{
|
|
Msg(eDLL_T::SERVER, "Rebooting RCON server...\n");
|
|
RCONServer()->Shutdown();
|
|
RCONServer()->Init();
|
|
}
|
|
#endif // !CLIENT_DLL
|
|
}
|
|
}
|
|
|
|
void LanguageChanged_f(IConVar* pConVar, const char* pOldString)
|
|
{
|
|
if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName()))
|
|
{
|
|
const char* pNewString = pConVarRef->GetString();
|
|
|
|
if (strcmp(pOldString, pConVarRef->GetString()) == NULL)
|
|
return; // Same language.
|
|
|
|
if (!Localize_IsLanguageSupported(pNewString))
|
|
{
|
|
// if new text isn't valid but the old value is, reset the value
|
|
if (Localize_IsLanguageSupported(pOldString))
|
|
pNewString = pOldString;
|
|
else
|
|
{
|
|
// this shouldn't really happen, but if neither the old nor new values are valid, set to english
|
|
Assert(0);
|
|
pNewString = g_LanguageNames[0];
|
|
}
|
|
}
|
|
|
|
pConVarRef->SetValue(pNewString);
|
|
g_MasterServer.SetLanguage(pNewString);
|
|
}
|
|
}
|
|
|
|
#ifndef DEDICATED
|
|
/*
|
|
=====================
|
|
Mat_CrossHair_f
|
|
|
|
Print the material under the crosshair.
|
|
=====================
|
|
*/
|
|
void Mat_CrossHair_f(const CCommand& args)
|
|
{
|
|
CMaterialGlue* material = v_GetMaterialAtCrossHair();
|
|
if (material)
|
|
{
|
|
Msg(eDLL_T::MS, "______________________________________________________________\n");
|
|
Msg(eDLL_T::MS, "-+ Material --------------------------------------------------\n");
|
|
Msg(eDLL_T::MS, " |-- ADDR: '%llX'\n", material);
|
|
Msg(eDLL_T::MS, " |-- GUID: '%llX'\n", material->assetGuid);
|
|
Msg(eDLL_T::MS, " |-- Num Streaming Textures: '%d'\n", material->numStreamingTextureHandles);
|
|
Msg(eDLL_T::MS, " |-- Material width: '%d'\n", material->width);
|
|
Msg(eDLL_T::MS, " |-- Material height: '%d'\n", material->height);
|
|
Msg(eDLL_T::MS, " |-- Samplers: '%08X'\n", material->samplers);
|
|
|
|
std::function<void(CMaterialGlue*, const char*)> fnPrintChild = [](CMaterialGlue* material, const char* print)
|
|
{
|
|
Msg(eDLL_T::MS, " |-+\n");
|
|
Msg(eDLL_T::MS, " | |-+ Child material ----------------------------------------\n");
|
|
Msg(eDLL_T::MS, print, material);
|
|
Msg(eDLL_T::MS, " | |-- GUID: '%llX'\n", material->assetGuid);
|
|
Msg(eDLL_T::MS, " | |-- Material name: '%s'\n", material->name);
|
|
};
|
|
|
|
Msg(eDLL_T::MS, " |-- Material name: '%s'\n", material->name);
|
|
Msg(eDLL_T::MS, " |-- Material surface name 1: '%s'\n", material->surfaceProp);
|
|
Msg(eDLL_T::MS, " |-- Material surface name 2: '%s'\n", material->surfaceProp2);
|
|
Msg(eDLL_T::MS, " |-- DX buffer: '%llX'\n", material->dxBuffer);
|
|
Msg(eDLL_T::MS, " |-- DX buffer VFTable: '%llX'\n", material->unkD3DPointer);
|
|
|
|
material->depthShadowMaterial
|
|
? fnPrintChild(material->depthShadowMaterial, " | |-+ DepthShadow: '%llX'\n")
|
|
: Msg(eDLL_T::MS, " | |-+ DepthShadow: 'NULL'\n");
|
|
material->depthPrepassMaterial
|
|
? fnPrintChild(material->depthPrepassMaterial, " | |-+ DepthPrepass: '%llX'\n")
|
|
: Msg(eDLL_T::MS, " | |-+ DepthPrepass: 'NULL'\n");
|
|
material->depthVSMMaterial
|
|
? fnPrintChild(material->depthVSMMaterial, " | |-+ DepthVSM: '%llX'\n")
|
|
: Msg(eDLL_T::MS, " | |-+ DepthVSM: 'NULL'\n");
|
|
material->depthShadowTightMaterial
|
|
? fnPrintChild(material->depthShadowTightMaterial, " | |-+ DepthShadowTight: '%llX'\n")
|
|
: Msg(eDLL_T::MS, " | |-+ DepthShadowTight: 'NULL'\n");
|
|
material->colpassMaterial
|
|
? fnPrintChild(material->colpassMaterial, " | |-+ ColPass: '%llX'\n")
|
|
: Msg(eDLL_T::MS, " | |-+ ColPass: 'NULL'\n");
|
|
|
|
Msg(eDLL_T::MS, "-+ Texture GUID map ------------------------------------------\n");
|
|
Msg(eDLL_T::MS, " |-- Texture handles: '%llX'\n", material->textureHandles);
|
|
Msg(eDLL_T::MS, " |-- Streaming texture handles: '%llX'\n", material->streamingTextureHandles);
|
|
|
|
Msg(eDLL_T::MS, "--------------------------------------------------------------\n");
|
|
}
|
|
else
|
|
{
|
|
Msg(eDLL_T::MS, "%s: No material found >:(\n", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Line_f
|
|
|
|
Draws a line at
|
|
start<x1 y1 z1> end<x2 y2 z2>.
|
|
=====================
|
|
*/
|
|
void Line_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() != 7)
|
|
{
|
|
Msg(eDLL_T::CLIENT, "Usage 'line': start(vector) end(vector)\n");
|
|
return;
|
|
}
|
|
|
|
Vector3D start, end;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
start[i] = float(atof(args[i + 1]));
|
|
end[i] = float(atof(args[i + 4]));
|
|
}
|
|
|
|
g_pDebugOverlay->AddLineOverlay(start, end, 255, 255, 0, !r_debug_draw_depth_test.GetBool(), 100);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Sphere_f
|
|
|
|
Draws a sphere at origin(x1 y1 z1)
|
|
radius(float) theta(int) phi(int).
|
|
=====================
|
|
*/
|
|
void Sphere_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() != 7)
|
|
{
|
|
Msg(eDLL_T::CLIENT, "Usage 'sphere': origin(vector) radius(float) theta(int) phi(int)\n");
|
|
return;
|
|
}
|
|
|
|
Vector3D start;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
start[i] = float(atof(args[i + 1]));
|
|
}
|
|
|
|
float radius = float(atof(args[4]));
|
|
int theta = atoi(args[5]);
|
|
int phi = atoi(args[6]);
|
|
|
|
g_pDebugOverlay->AddSphereOverlay(start, radius, theta, phi, 20, 210, 255, 0, 100);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Capsule_f
|
|
|
|
Draws a capsule at start<x1 y1 z1>
|
|
end<x2 y2 z2> radius <x3 y3 z3>.
|
|
=====================
|
|
*/
|
|
void Capsule_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() != 10)
|
|
{
|
|
Msg(eDLL_T::CLIENT, "Usage 'capsule': start(vector) end(vector) radius(vector)\n");
|
|
return;
|
|
}
|
|
|
|
Vector3D start, end, radius;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
start[i] = float(atof(args[i + 1]));
|
|
end[i] = float(atof(args[i + 4]));
|
|
radius[i] = float(atof(args[i + 7]));
|
|
}
|
|
g_pDebugOverlay->AddCapsuleOverlay(start, end, radius, { 0,0,0 }, { 0,0,0 }, 141, 233, 135, 0, 100);
|
|
}
|
|
#endif // !DEDICATED
|
|
|
|
// TODO: move to other file?
|
|
static ConVar bhit_depth_test("bhit_depth_test", "0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use depth test for bullet ray trace overlay");
|
|
static ConVar bhit_abs_origin("bhit_abs_origin", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Draw entity's predicted abs origin upon bullet impact for trajectory debugging (requires 'r_visualizetraces' to be set!)");
|
|
/*
|
|
=====================
|
|
BHit_f
|
|
|
|
Bullet trajectory tracing
|
|
from shooter to target entity.
|
|
=====================
|
|
*/
|
|
void BHit_f(const CCommand& args)
|
|
{
|
|
#ifndef CLIENT_DLL // Stubbed to suppress server warnings as this is a GAMEDLL command!
|
|
if (args.ArgC() != 9)
|
|
return;
|
|
|
|
if (!bhit_enable->GetBool())
|
|
return;
|
|
|
|
if (sv_visualizetraces->GetBool())
|
|
{
|
|
Vector3D vecAbsStart;
|
|
Vector3D vecAbsEnd;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
vecAbsStart[i] = float(atof(args[i + 4]));
|
|
|
|
QAngle vecBulletAngles;
|
|
for (int i = 0; i < 2; ++i)
|
|
vecBulletAngles[i] = float(atof(args[i + 7]));
|
|
|
|
vecBulletAngles.z = 180.f; // Flipped axis.
|
|
AngleVectors(vecBulletAngles, &vecAbsEnd);
|
|
|
|
vecAbsEnd.MulAdd(vecAbsStart, vecAbsEnd, MAX_COORD_RANGE);
|
|
|
|
Ray_t ray(vecAbsStart, vecAbsEnd);
|
|
trace_t trace;
|
|
|
|
g_pEngineTraceServer->TraceRay(ray, TRACE_MASK_NPCWORLDSTATIC, &trace);
|
|
|
|
g_pDebugOverlay->AddLineOverlay(trace.startpos, trace.endpos, 0, 255, 0, !bhit_depth_test.GetBool(), sv_visualizetraces_duration->GetFloat());
|
|
g_pDebugOverlay->AddLineOverlay(trace.endpos, vecAbsEnd, 255, 0, 0, !bhit_depth_test.GetBool(), sv_visualizetraces_duration->GetFloat());
|
|
}
|
|
#endif // !CLIENT_DLL
|
|
|
|
#ifndef DEDICATED
|
|
if (bhit_abs_origin.GetBool() && r_visualizetraces->GetBool())
|
|
{
|
|
const int iEnt = atoi(args[2]);
|
|
if (const IClientEntity* pEntity = g_pClientEntityList->GetClientEntity(iEnt))
|
|
{
|
|
g_pDebugOverlay->AddSphereOverlay( // Render a debug sphere at the client's predicted entity origin.
|
|
pEntity->GetAbsOrigin(), 10.f, 8, 6, 20, 60, 255, 0, r_visualizetraces_duration->GetFloat());
|
|
}
|
|
}
|
|
#endif // !DEDICATED
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CVHelp_f
|
|
|
|
Show help text for a
|
|
particular convar/concommand
|
|
=====================
|
|
*/
|
|
void CVHelp_f(const CCommand& args)
|
|
{
|
|
cv->CvarHelp(args);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CVList_f
|
|
|
|
List all ConCommandBases
|
|
=====================
|
|
*/
|
|
void CVList_f(const CCommand& args)
|
|
{
|
|
cv->CvarList(args);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CVDiff_f
|
|
|
|
List all ConVar's
|
|
who's values deviate
|
|
from default value
|
|
=====================
|
|
*/
|
|
void CVDiff_f(const CCommand& args)
|
|
{
|
|
cv->CvarDifferences(args);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CVFlag_f
|
|
|
|
List all ConVar's
|
|
with specified flags
|
|
=====================
|
|
*/
|
|
void CVFlag_f(const CCommand& args)
|
|
{
|
|
cv->CvarFindFlags_f(args);
|
|
}
|
|
|
|
#ifndef DEDICATED
|
|
static double s_flScriptExecTimeBase = 0.0f;
|
|
static int s_nScriptExecCount = 0;
|
|
#endif // !DEDICATED
|
|
/*
|
|
=====================
|
|
Cmd_Exec_f
|
|
|
|
executes a cfg file
|
|
=====================
|
|
*/
|
|
#ifndef DEDICATED
|
|
static ConVar sv_quota_scriptExecsPerSecond("sv_quota_scriptExecsPerSecond", "3", FCVAR_REPLICATED | FCVAR_RELEASE,
|
|
"How many script executions per second clients are allowed to submit, 0 to disable the limitation thereof.", true, 0.f, false, 0.f);
|
|
#endif // !DEDICATED
|
|
|
|
void Cmd_Exec_f(const CCommand& args)
|
|
{
|
|
#ifndef DEDICATED
|
|
// Prevent users from running neo strafe commands and other quick hacks.
|
|
// TODO: when reBar becomes a thing, we should verify this function and
|
|
// flag users that patch them out.
|
|
if (!ThreadInServerFrameThread() && g_pClientState->IsActive())
|
|
{
|
|
const int execQuota = sv_quota_scriptExecsPerSecond.GetInt();
|
|
|
|
if (execQuota > 0)
|
|
{
|
|
const double flCurrentTime = Plat_FloatTime();
|
|
|
|
// Reset every second.
|
|
if ((flCurrentTime - s_flScriptExecTimeBase) > 1.0)
|
|
{
|
|
s_flScriptExecTimeBase = flCurrentTime;
|
|
s_nScriptExecCount = 0;
|
|
}
|
|
|
|
if (s_nScriptExecCount >= execQuota)
|
|
{
|
|
DevWarning(eDLL_T::ENGINE, "Client is simulating and exec count = %d of %d; dropped exec command: %s\n",
|
|
s_nScriptExecCount, execQuota, args.ArgS());
|
|
|
|
return;
|
|
}
|
|
|
|
s_nScriptExecCount++;
|
|
}
|
|
}
|
|
#endif // !DEDICATED
|
|
v__Cmd_Exec_f(args);
|
|
}
|
|
|
|
|
|
void VCallback::Detour(const bool bAttach) const
|
|
{
|
|
DetourSetup(&v__Cmd_Exec_f, &Cmd_Exec_f, bAttach);
|
|
}
|