434 lines
No EOL
9.8 KiB
C++
434 lines
No EOL
9.8 KiB
C++
#define GAME_DLL
|
|
#include "cbase.h"
|
|
#include "main.h"
|
|
#include "plugin.h"
|
|
#include "sigscan.h"
|
|
#include "chook.h"
|
|
#include "dlls/iplayerinfo.h"
|
|
#include "filesystem.h"
|
|
#include "eiface.h"
|
|
#include "threadtools.h"
|
|
#include "utlvector.h"
|
|
#include "vphysics_interface.h"
|
|
#include "steam/steam_api.h"
|
|
#include "inetchannelinfo.h"
|
|
#include "inetchannel.h"
|
|
#include "inetmessage.h"
|
|
#include "netmessage.h"
|
|
#include "bitbuf.h"
|
|
#include "baselib.h"
|
|
#include "memdbgon.h"
|
|
|
|
class CBaseClient;
|
|
static bool _IsSecurePath(const char* pPath);
|
|
bool IsValidModel_Internal(const char* pPath);
|
|
bool IsValidModel_Hook(const char*);
|
|
void Nocollide_Hook(IPhysicsObject*,IPhysicsObject*);
|
|
bool __fastcall Process_Hook(void* thisptr,void*);
|
|
|
|
typedef struct {
|
|
char name[260];
|
|
char value[260];
|
|
} cvar_t;
|
|
|
|
typedef struct {
|
|
void* vtable;
|
|
bool m_bReliable;
|
|
void* chan;
|
|
void* handler;
|
|
CUtlVector<cvar_t> cvars;
|
|
} net_setconvar_t;
|
|
|
|
typedef struct {
|
|
void* p1;
|
|
void* p2;
|
|
} pair_t;
|
|
|
|
inline bool IsPairsEqual(pair_t& a,pair_t& b)
|
|
{
|
|
return ((a.p1 == b.p1) || (a.p1 == b.p2)
|
|
|| (a.p2 == b.p1) || (a.p2 == b.p2));
|
|
}
|
|
|
|
typedef bool (__thiscall *PrcSetConVar_t)(CBaseClient*,net_setconvar_t*);
|
|
static bool __fastcall PrcSetConVar_Hook(CBaseClient*,void*,net_setconvar_t*);
|
|
|
|
typedef void (*Nocollide_t)(void*,void*);
|
|
typedef bool (__thiscall* Process_t)(void*);
|
|
typedef void (*Disconnect_t)(void*,const char*,...);
|
|
|
|
DECLARE_PLUGIN(CSDKFixes)
|
|
virtual bool Load(CreateInterfaceFn,
|
|
CreateInterfaceFn);
|
|
virtual bool LuaInit(lua_State*);
|
|
virtual void Unload();
|
|
virtual void LevelShutdown();
|
|
|
|
static IsValidModel_t s_pIsValidModel;
|
|
static PrcSetConVar_t s_pPrcSetConVar;
|
|
static Nocollide_t s_pNocollide;
|
|
static Process_t s_pProcess;
|
|
static Disconnect_t s_pDisconnect;
|
|
|
|
CUtlVector<pair_t> m_Nocollides;
|
|
private:
|
|
CHook* m_pIsValidModelHook;
|
|
CHook* m_pSetConVarHook;
|
|
CHook* m_pNocollideHook;
|
|
CHook* m_pProcessHook;
|
|
END_PLUGIN(CSDKFixes,"sdk_fixes");
|
|
|
|
IsValidModel_t CSDKFixes::s_pIsValidModel = NULL;
|
|
PrcSetConVar_t CSDKFixes::s_pPrcSetConVar = NULL;
|
|
Nocollide_t CSDKFixes::s_pNocollide = NULL;
|
|
Process_t CSDKFixes::s_pProcess = NULL;
|
|
Disconnect_t CSDKFixes::s_pDisconnect = NULL;
|
|
IPlayerInfoManager* playerinfomanager = NULL;
|
|
IVEngineServer* engine = NULL;
|
|
IFileSystem* filesystem = NULL;
|
|
|
|
class CGameServer;
|
|
|
|
class CBaseClient
|
|
{
|
|
public:
|
|
inline edict_t* GetEdict()
|
|
{
|
|
return *((edict_t**)this+145);
|
|
}
|
|
|
|
inline int GetUserId()
|
|
{
|
|
return *((int*)this+5);
|
|
}
|
|
|
|
inline const char* GetVar(const char* pKey)
|
|
{
|
|
return m_pConVars->GetString(pKey);
|
|
}
|
|
|
|
inline INetChannel* GetNetChannel()
|
|
{
|
|
return *((INetChannel**)this+54);
|
|
}
|
|
|
|
inline int GetSignonState()
|
|
{
|
|
return *((int*)this+55);
|
|
}
|
|
|
|
inline char* GetName()
|
|
{
|
|
return (char*)this+24;
|
|
}
|
|
|
|
void SetName(const char* pName)
|
|
{
|
|
strncpy(((char*)this+24),pName,32);
|
|
}
|
|
|
|
inline CSteamID* GetSteamId()
|
|
{
|
|
return *(CSteamID**)((char*)this+120);
|
|
}
|
|
|
|
inline void SetVar(const char* pKey,
|
|
const char* pValue)
|
|
{
|
|
m_pConVars->SetString(pKey,pValue);
|
|
//m_bConvarsChanged = false;
|
|
}
|
|
|
|
inline void SetConVarsChanged(bool bVal)
|
|
{
|
|
m_bConvarsChanged = bVal;
|
|
}
|
|
|
|
inline int GetClientIndex()
|
|
{
|
|
return *(int*)((char*)this+0xC);
|
|
}
|
|
|
|
char m_szTmp[160];
|
|
KeyValues* m_pConVars;
|
|
bool m_bConvarsChanged;
|
|
CGameServer* m_Server;
|
|
};
|
|
|
|
class CGameServer
|
|
{
|
|
public:
|
|
CBaseClient* GetClient(edict_t* pEdict)
|
|
{
|
|
for(int i = 0; i < m_Clients.Count(); i++)
|
|
if(m_Clients[i]->GetEdict() == pEdict)
|
|
return m_Clients[i];
|
|
return NULL;
|
|
}
|
|
|
|
char m_szTmp[252];
|
|
int m_nUserId;
|
|
int m_nMaxclients;
|
|
int m_nSpawnCount;
|
|
float m_fTickInterval;
|
|
CUtlVector<CBaseClient*> m_Clients;
|
|
};
|
|
|
|
CGameServer* gameserver = NULL;
|
|
|
|
bool CSDKFixes::Load(CreateInterfaceFn appFactory,
|
|
CreateInterfaceFn gameFactory)
|
|
{
|
|
DWORD dwIsValidModel,dwSetConVar,dwCall,dwNocollide,dwProcess;
|
|
filesystem = (IFileSystem*)appFactory(
|
|
FILESYSTEM_INTERFACE_VERSION,0);
|
|
engine = (IVEngineServer*)appFactory(
|
|
INTERFACEVERSION_VENGINESERVER,0);
|
|
playerinfomanager = (IPlayerInfoManager*)gameFactory(
|
|
INTERFACEVERSION_PLAYERINFOMANAGER,0);
|
|
|
|
if(!filesystem || !engine || !playerinfomanager)
|
|
{
|
|
Warning("Failed appFactory/gameFactory (%p %p %p)\n",
|
|
filesystem,engine,playerinfomanager);
|
|
return false;
|
|
}
|
|
|
|
DECLARE_SIGSCAN(dwIsValidModel,"server.dll",
|
|
ISVALIDMODEL_SIG,ISVALIDMODEL_MASK,0,
|
|
SigScan::CSigScan::SIG_FUNCTION);
|
|
DECLARE_SIGSCAN(dwCall,"server.dll",PCALL_SIG,
|
|
PCALL_MASK,0,SigScan::CSigScan::SIG_FUNCTION);
|
|
DECLARE_SIGSCAN(gameserver,"engine.dll",GAMESERVER_SIG,
|
|
GAMESERVER_MASK,GAMESERVER_OFFSET,
|
|
SigScan::CSigScan::SIG_PTR);
|
|
DECLARE_SIGSCAN(dwSetConVar,"engine.dll",SETCONVAR_SIG,
|
|
SETCONVAR_MASK,0,SigScan::CSigScan::SIG_FUNCTION);
|
|
DECLARE_SIGSCAN(dwProcess,"engine.dll",CLCPROCESS_SIG,
|
|
CLCPROCESS_MASK,0,SigScan::CSigScan::SIG_FUNCTION);
|
|
DECLARE_SIGSCAN(s_pDisconnect,"engine.dll",CLDISCONNECT_SIG,
|
|
CLDISCONNECT_MASK,0,SigScan::CSigScan::SIG_FUNCTION);
|
|
SigScan::Scan();
|
|
dwNocollide = FetchCall(dwCall);
|
|
|
|
Msg("gameserver %p\n",gameserver);
|
|
m_pIsValidModelHook = new CHook(dwIsValidModel,
|
|
ISVALIDMODEL_HOOKSIZE);
|
|
s_pIsValidModel = (IsValidModel_t)m_pIsValidModelHook
|
|
->HookFunction((DWORD)IsValidModel_Hook);
|
|
|
|
m_pSetConVarHook = new CHook(dwSetConVar,
|
|
SETCONVAR_HOOKSIZE);
|
|
s_pPrcSetConVar = (PrcSetConVar_t)m_pSetConVarHook
|
|
->HookFunction((DWORD)&PrcSetConVar_Hook);
|
|
|
|
m_pNocollideHook = new CHook(dwNocollide,PCALL_HOOKSIZE);
|
|
s_pNocollide = (Nocollide_t)m_pNocollideHook
|
|
->HookFunction((DWORD)&Nocollide_Hook);
|
|
|
|
m_pProcessHook = new CHook(dwProcess,CLCPROCESS_HOOKSIZE);
|
|
s_pProcess = (Process_t)m_pProcessHook
|
|
->HookFunction((DWORD)&Process_Hook);
|
|
Msg("GetPlayerUserid %p\n",(*(void***)engine)[15]);
|
|
return true;
|
|
}
|
|
|
|
bool CSDKFixes::LuaInit(lua_State* L)
|
|
{
|
|
CLuaLibrary::Init(L);
|
|
return true;
|
|
}
|
|
|
|
void CSDKFixes::Unload()
|
|
{
|
|
if(m_pIsValidModelHook)
|
|
delete m_pIsValidModelHook;
|
|
if(m_pSetConVarHook)
|
|
delete m_pSetConVarHook;
|
|
if(m_pNocollideHook)
|
|
delete m_pNocollideHook;
|
|
}
|
|
|
|
void CSDKFixes::LevelShutdown()
|
|
{
|
|
m_Nocollides.RemoveAll();
|
|
}
|
|
|
|
typedef enum {
|
|
VALUE_NONE,
|
|
VALUE_MODEL,
|
|
VALUE_MATERIAL,
|
|
} vartype_t;
|
|
|
|
typedef struct {
|
|
const char* pName;
|
|
const char* pDefault;
|
|
vartype_t type;
|
|
} userinfo_t;
|
|
|
|
userinfo_t g_UserInfos[] = {
|
|
{"gm_wheel_model","models/props_junk/sawblade001a.mdl",VALUE_MODEL},
|
|
{NULL,NULL,VALUE_NONE}
|
|
};
|
|
|
|
static bool __fastcall PrcSetConVar_Hook(CBaseClient* pClient,
|
|
void* edx,net_setconvar_t* pMsg)
|
|
{
|
|
bool bOk = CSDKFixes::s_pPrcSetConVar(pClient,pMsg);
|
|
for(int i = 0; i < pMsg->cvars.Count(); i++)
|
|
{
|
|
for(userinfo_t* j = g_UserInfos; j->pName; j++)
|
|
{
|
|
cvar_t& cv = pMsg->cvars[i];
|
|
if(strcmp(cv.name,j->pName)
|
|
|| j->type != VALUE_MODEL)
|
|
continue;
|
|
if(!IsValidModel_Internal(cv.value))
|
|
{
|
|
Msg("Client %d tried to send invalid user value (%s)!\n",
|
|
pClient->GetUserId(),cv.name);
|
|
strcpy_s(cv.value,j->pDefault);
|
|
}
|
|
}
|
|
}
|
|
return bOk;
|
|
}
|
|
|
|
inline bool _CheckExtension(const char* pPath,
|
|
const char* pExt)
|
|
{
|
|
const char* pTmp;
|
|
if(!(pTmp = strrchr(pPath,'.')))
|
|
return false;
|
|
return !strcmp(pTmp,pExt);
|
|
}
|
|
|
|
static bool _IsSecurePath(const char* pPath)
|
|
{
|
|
static char s_cBanned[] = {
|
|
'\t','\r','\b','\n',':'};
|
|
size_t uLen = strlen(pPath);
|
|
if(pPath[0] == '/'
|
|
|| pPath[0] == '\\')
|
|
return false;
|
|
for(size_t i = 0; i < uLen; i++)
|
|
{
|
|
if(pPath[i] == s_cBanned[
|
|
i%sizeof(s_cBanned)])
|
|
return false;
|
|
if(pPath[i] == '.' && pPath[i+1] == '.')
|
|
return false;
|
|
}
|
|
|
|
return !_CheckExtension(pPath,".bsp");
|
|
}
|
|
|
|
bool IsValidModel_Internal(const char* pPath)
|
|
{
|
|
if(!_IsSecurePath(pPath)) return false;
|
|
if(!_CheckExtension(pPath,".mdl")) return false;
|
|
if(!filesystem->FileExists(pPath))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool IsValidModel_Hook(const char* pPath)
|
|
{
|
|
if(!IsValidModel_Internal(pPath))
|
|
return false;
|
|
return CSDKFixes::s_pIsValidModel(pPath);
|
|
}
|
|
|
|
void Nocollide_Hook(IPhysicsObject* obj1,IPhysicsObject* obj2)
|
|
{
|
|
pair_t pr;
|
|
pr.p1 = obj1;
|
|
pr.p2 = obj2;
|
|
for(int i = 0; i < s_CSDKFixes.m_Nocollides.Count(); i++)
|
|
{
|
|
pair_t pr2 = s_CSDKFixes.m_Nocollides[i];
|
|
if(IsPairsEqual(pr,pr2)) return;
|
|
}
|
|
s_CSDKFixes.m_Nocollides.AddToTail(pr);
|
|
CSDKFixes::s_pNocollide(obj1,obj2);
|
|
}
|
|
|
|
const char* RenderSteamId(CSteamID* id)
|
|
{
|
|
static char szBuf[64];
|
|
uint64 sid = id->ConvertToUint64();
|
|
int x,y,z;
|
|
//STEAM_X:Y:Z
|
|
x = sid>>61;
|
|
y = sid&1;
|
|
z = (sid>>1)&0x7FFFFFFF;
|
|
Q_snprintf(szBuf,64,"STEAM_%d:%d:%d",x,y,z);
|
|
return szBuf;
|
|
}
|
|
|
|
bool __fastcall Process_Hook(void* thisptr,void*)
|
|
{
|
|
INetMessage* pMsg = (INetMessage*)thisptr;
|
|
INetChannel* netchan = pMsg->GetNetChannel();
|
|
for(int i = 0; i < gameserver->m_Clients.Count(); i++)
|
|
{
|
|
CBaseClient* cl = gameserver->m_Clients[i];
|
|
if(cl->GetNetChannel() == netchan)
|
|
{
|
|
if(cl->GetSignonState() > 3)
|
|
{
|
|
char szBuf[128];
|
|
Q_snprintf(szBuf,sizeof(szBuf),"Player %s (%s) attempted to bug sprays, lets kick him\n",
|
|
cl->GetName(),RenderSteamId(cl->GetSteamId()));
|
|
|
|
Log(szBuf);
|
|
CSDKFixes::s_pDisconnect((char*)cl+0x4,szBuf);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CSDKFixes::s_pProcess(thisptr);
|
|
}
|
|
|
|
DECLARE_LIBRARY("_player")
|
|
DECLARE_TABLE(_G)
|
|
|
|
inline CBaseClient* GetPlayer(lua_State* L)
|
|
{
|
|
edict_t* pPly;
|
|
CBaseClient* pClient;
|
|
if(!(pPly = engine->PEntityOfEntIndex((int)lua_tonumber(L,1))))
|
|
luaL_error(L,"Wrong player index!");
|
|
if(!(pClient = gameserver->GetClient(pPly)))
|
|
luaL_error(L,"Edict isn't a client (%d)!",(int)lua_tonumber(L,1));
|
|
return pClient;
|
|
}
|
|
|
|
DECLARE_FUNCTION(_G,_SetClientConVar_String)
|
|
{
|
|
luaL_checktype(L,1,LUA_TNUMBER);
|
|
luaL_checktype(L,2,LUA_TSTRING);
|
|
luaL_checktype(L,3,LUA_TSTRING);
|
|
CBaseClient* pPlayer = GetPlayer(L);
|
|
pPlayer->SetVar(lua_tostring(L,2),lua_tostring(L,3));
|
|
pPlayer->SetConVarsChanged(true);
|
|
return 0;
|
|
}
|
|
|
|
DECLARE_FUNCTION(_G,_PlayerGetName)
|
|
{
|
|
luaL_checktype(L,1,LUA_TNUMBER);
|
|
lua_pushstring(L,GetPlayer(L)->GetVar("name"));
|
|
return 1;
|
|
}
|
|
|
|
DECLARE_FUNCTION(_G,_PlayerSetName)
|
|
{
|
|
luaL_checktype(L,1,LUA_TNUMBER);
|
|
luaL_checktype(L,2,LUA_TSTRING);
|
|
CBaseClient* pPlayer = GetPlayer(L);
|
|
pPlayer->SetVar("name",lua_tostring(L,2));
|
|
pPlayer->SetConVarsChanged(true);
|
|
return 0;
|
|
} |