2012-12-15 06:38:27 +08:00
|
|
|
#include "l4dtoolz_mm.h"
|
2024-05-05 19:08:27 +08:00
|
|
|
#include "game_offsets.h"
|
|
|
|
#include "memutils.h"
|
2022-05-11 00:05:13 +08:00
|
|
|
#include "icommandline.h"
|
2024-05-05 19:08:27 +08:00
|
|
|
#include "server_class.h"
|
|
|
|
#include "sourcehook.h"
|
|
|
|
#include "matchmaking/imatchframework.h"
|
2012-12-15 06:38:27 +08:00
|
|
|
|
|
|
|
l4dtoolz g_l4dtoolz;
|
2024-05-05 19:08:27 +08:00
|
|
|
IServerGameDLL* gamedll = NULL;
|
|
|
|
IServerGameClients* gameclients = NULL;
|
2012-12-15 06:38:27 +08:00
|
|
|
IVEngineServer* engine = NULL;
|
2024-05-05 19:08:27 +08:00
|
|
|
IMatchFramework* g_pMatchFramework = NULL;
|
2021-06-30 15:06:37 +08:00
|
|
|
ICvar* g_pCVar = NULL;
|
2024-05-05 19:08:27 +08:00
|
|
|
IServer* g_pGameIServer = NULL;
|
|
|
|
void* g_pGameRules = nullptr;
|
|
|
|
int m_numGameSlots = -1;
|
2012-12-15 06:38:27 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
SH_DECL_HOOK1_void(IServerGameDLL, ApplyGameSettings, SH_NOATTRIB, 0, KeyValues*);
|
|
|
|
SH_DECL_HOOK0(IMatchTitle, GetTotalNumPlayersSupported, SH_NOATTRIB, 0, int);
|
|
|
|
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool);
|
|
|
|
SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, 0);
|
|
|
|
SH_DECL_MANUALHOOK0(CTerrorGameRules_GetMaxHumanPlayers, maxhuman_idx, 0, 0, int);
|
|
|
|
SH_DECL_MANUALHOOK2_void(CBaseServer_ReplyReservationRequest, reservation_idx, 0, 0, netadr_s&, CBitRead&);
|
2012-12-15 06:38:27 +08:00
|
|
|
|
2024-05-05 20:05:40 +08:00
|
|
|
ConVar sv_maxplayers("sv_maxplayers", "-1", FCVAR_SPONLY|FCVAR_NOTIFY, "Max Human Players", true, -1, true, 32, l4dtoolz::OnChangeMaxplayers);
|
|
|
|
ConVar sv_force_unreserved("sv_force_unreserved", "0", FCVAR_SPONLY|FCVAR_NOTIFY, "Disallow lobby reservation cookie", true, 0, true, 1, l4dtoolz::OnChangeUnreserved);
|
2012-12-15 06:38:27 +08:00
|
|
|
|
|
|
|
void l4dtoolz::OnChangeMaxplayers ( IConVar *var, const char *pOldValue, float flOldValue )
|
|
|
|
{
|
2013-01-21 21:39:57 +08:00
|
|
|
int new_value = ((ConVar*)var)->GetInt();
|
|
|
|
int old_value = atoi(pOldValue);
|
2024-05-05 19:08:27 +08:00
|
|
|
if (g_pGameIServer == NULL) {
|
|
|
|
Msg("g_pGameIServer pointer is not available\n");
|
2012-12-15 06:38:27 +08:00
|
|
|
return;
|
|
|
|
}
|
2013-01-21 21:39:57 +08:00
|
|
|
if(new_value != old_value) {
|
2013-01-22 04:27:24 +08:00
|
|
|
if(new_value >= 0) {
|
2024-05-05 19:08:27 +08:00
|
|
|
m_numGameSlots = new_value;
|
|
|
|
*(int*)(((uint**)g_pGameIServer)+slots_offs) = m_numGameSlots;
|
2013-01-21 21:39:57 +08:00
|
|
|
} else {
|
2024-05-05 19:08:27 +08:00
|
|
|
m_numGameSlots = -1;
|
2013-01-21 21:39:57 +08:00
|
|
|
}
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
}
|
2013-01-21 21:39:57 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
void l4dtoolz::OnChangeUnreserved ( IConVar *var, const char *pOldValue, float flOldValue )
|
2012-12-15 06:38:27 +08:00
|
|
|
{
|
2013-01-21 21:39:57 +08:00
|
|
|
int new_value = ((ConVar*)var)->GetInt();
|
|
|
|
int old_value = atoi(pOldValue);
|
2024-05-05 19:08:27 +08:00
|
|
|
if (g_pGameIServer == NULL) {
|
|
|
|
Msg("g_pGameIServer pointer is not available\n");
|
2012-12-15 06:38:27 +08:00
|
|
|
return;
|
|
|
|
}
|
2013-01-21 21:39:57 +08:00
|
|
|
if(new_value != old_value) {
|
|
|
|
if(new_value == 1) {
|
2024-05-05 19:08:27 +08:00
|
|
|
engine->ServerCommand("sv_allow_lobby_connect_only 0\n");
|
2013-01-21 21:39:57 +08:00
|
|
|
}
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
void Hook_ApplyGameSettings(KeyValues *pKV)
|
2012-12-15 06:38:27 +08:00
|
|
|
{
|
2024-05-05 19:08:27 +08:00
|
|
|
if (!pKV) {
|
2012-12-15 06:38:27 +08:00
|
|
|
return;
|
|
|
|
}
|
2024-05-05 19:08:27 +08:00
|
|
|
m_numGameSlots = sv_maxplayers.GetInt();
|
|
|
|
if (m_numGameSlots == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pKV->SetInt("members/numSlots", m_numGameSlots);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Hook_ReplyReservationRequest(netadr_s& adr, CBitRead& inmsg)
|
|
|
|
{
|
|
|
|
if (sv_force_unreserved.GetInt()) {
|
|
|
|
RETURN_META(MRES_SUPERCEDE);
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
2024-05-05 19:08:27 +08:00
|
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Hook_GetMaxHumanPlayers()
|
|
|
|
{
|
|
|
|
if (m_numGameSlots > 0) {
|
|
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, m_numGameSlots);
|
|
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, m_numGameSlots);
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
PLUGIN_EXPOSE(l4dtoolz, g_l4dtoolz);
|
|
|
|
|
|
|
|
bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
|
|
|
{
|
|
|
|
PLUGIN_SAVEVARS();
|
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
GET_V_IFACE_CURRENT(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
|
|
|
|
GET_V_IFACE_CURRENT(GetServerFactory, gameclients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS);
|
2012-12-15 06:38:27 +08:00
|
|
|
GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
|
2024-05-05 19:08:27 +08:00
|
|
|
GET_V_IFACE_CURRENT(GetEngineFactory, g_pMatchFramework, IMatchFramework, IMATCHFRAMEWORK_VERSION_STRING);
|
2021-06-30 15:06:37 +08:00
|
|
|
GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
|
2012-12-15 06:38:27 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
void* handle = NULL;
|
2024-05-05 19:13:16 +08:00
|
|
|
#if SH_SYS == SH_SYS_WIN32
|
2024-05-05 19:08:27 +08:00
|
|
|
if (!(handle=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient))) {
|
|
|
|
Warning("Failed to get address 'IVEngineServer::CreateFakeClient'\n");
|
|
|
|
} else {
|
|
|
|
g_pGameIServer = *reinterpret_cast<IServer **>(reinterpret_cast<unsigned char *>(handle)+sv_offs);
|
2012-12-15 06:38:27 +08:00
|
|
|
#else
|
2024-05-05 19:08:27 +08:00
|
|
|
if (!(handle=dlopen(engine_dll, RTLD_LAZY))) {
|
|
|
|
Warning("Could't open library '%s'\n", engine_dll);
|
|
|
|
} else {
|
|
|
|
g_pGameIServer = (IServer *)g_MemUtils.ResolveSymbol(handle, "sv");
|
|
|
|
dlclose(handle);
|
2013-07-09 17:57:40 +08:00
|
|
|
#endif
|
2024-05-05 19:08:27 +08:00
|
|
|
int* m_nMaxClientsLimit = (int*)(((uint**)g_pGameIServer)+maxplayers_offs);
|
|
|
|
if (*m_nMaxClientsLimit != 0x12) {
|
|
|
|
Warning("Couldn't patch maxplayers\n");
|
|
|
|
g_pGameIServer = NULL;
|
|
|
|
} else {
|
|
|
|
*m_nMaxClientsLimit = 0x20;
|
2022-05-10 23:52:38 +08:00
|
|
|
const char *pszCmdLineMax;
|
|
|
|
if(CommandLine()->CheckParm("-maxplayers", &pszCmdLineMax) || CommandLine()->CheckParm("+maxplayers", &pszCmdLineMax)) {
|
|
|
|
char command[32];
|
|
|
|
UTIL_Format(command, sizeof(command), "maxplayers %d\n", clamp(atoi(pszCmdLineMax), 1, 32));
|
|
|
|
engine->ServerCommand(command);
|
|
|
|
} else {
|
|
|
|
engine->ServerCommand("maxplayers 31\n");
|
|
|
|
}
|
2021-06-30 15:06:37 +08:00
|
|
|
engine->ServerExecute();
|
2013-01-21 17:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
2012-12-15 06:38:27 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
SH_ADD_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true);
|
|
|
|
SH_ADD_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false);
|
|
|
|
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true);
|
|
|
|
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false);
|
|
|
|
|
|
|
|
if (g_pGameIServer) {
|
|
|
|
SH_ADD_MANUALHOOK(CBaseServer_ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false);
|
|
|
|
} else {
|
|
|
|
Warning("g_pGameIServer pointer is not available\n");
|
2013-07-09 17:57:40 +08:00
|
|
|
}
|
2024-05-05 19:08:27 +08:00
|
|
|
|
|
|
|
ConVar_Register(0, this);
|
2012-12-15 06:38:27 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool l4dtoolz::Unload(char *error, size_t maxlen)
|
|
|
|
{
|
2024-05-05 19:08:27 +08:00
|
|
|
SH_REMOVE_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true);
|
|
|
|
SH_REMOVE_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false);
|
|
|
|
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true);
|
|
|
|
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false);
|
|
|
|
|
|
|
|
if (g_pGameIServer) {
|
|
|
|
SH_REMOVE_MANUALHOOK(CBaseServer_ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false);
|
|
|
|
}
|
2013-07-09 17:57:40 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
LevelShutdown();
|
|
|
|
ConVar_Unregister();
|
2013-01-21 21:39:57 +08:00
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool l4dtoolz::LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
|
|
|
|
{
|
|
|
|
g_pGameRules = nullptr;
|
|
|
|
|
|
|
|
ServerClass *pServerClass = UTIL_FindServerClass("CTerrorGameRulesProxy");
|
|
|
|
if (pServerClass) {
|
|
|
|
int i, iCount;
|
|
|
|
iCount = pServerClass->m_pTable->GetNumProps();
|
|
|
|
for (i = 0; i < iCount; i++) {
|
|
|
|
if (stricmp(pServerClass->m_pTable->GetProp(i)->GetName(), "terror_gamerules_data") == 0) {
|
|
|
|
g_pGameRules = (*pServerClass->m_pTable->GetProp(i)->GetDataTableProxyFn())(NULL, NULL, NULL, NULL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_pGameRules) {
|
|
|
|
SH_ADD_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false);
|
|
|
|
} else {
|
|
|
|
Warning("g_pGameRules pointer is not available\n");
|
|
|
|
}
|
2013-01-22 04:27:24 +08:00
|
|
|
|
2012-12-15 06:38:27 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
void l4dtoolz::LevelShutdown()
|
|
|
|
{
|
|
|
|
if (g_pGameRules) {
|
|
|
|
SH_REMOVE_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false);
|
|
|
|
g_pGameRules = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-10 23:52:38 +08:00
|
|
|
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
size_t len = vsnprintf(buffer, maxlength, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (len >= maxlength)
|
|
|
|
{
|
|
|
|
len = maxlength - 1;
|
|
|
|
buffer[len] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2024-05-05 19:08:27 +08:00
|
|
|
ServerClass *UTIL_FindServerClass(const char *classname)
|
|
|
|
{
|
|
|
|
ServerClass *sc = gamedll->GetAllServerClasses();
|
|
|
|
while (sc)
|
|
|
|
{
|
|
|
|
if (strcmp(classname, sc->GetName()) == 0)
|
|
|
|
{
|
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
sc = sc->m_pNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-15 06:38:27 +08:00
|
|
|
const char *l4dtoolz::GetLicense()
|
|
|
|
{
|
2021-06-30 15:06:37 +08:00
|
|
|
return "GPLv3";
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetVersion()
|
|
|
|
{
|
2024-05-05 19:08:27 +08:00
|
|
|
return "2.0.0";
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetDate()
|
|
|
|
{
|
|
|
|
return __DATE__;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetLogTag()
|
|
|
|
{
|
|
|
|
return "L4DToolZ";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetAuthor()
|
|
|
|
{
|
2021-06-30 15:06:37 +08:00
|
|
|
return "Accelerator, Ivailosp";
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetDescription()
|
|
|
|
{
|
2021-06-30 15:06:37 +08:00
|
|
|
return "Unlock the max player limit on L4D and L4D2";
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetName()
|
|
|
|
{
|
|
|
|
return "L4DToolZ";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *l4dtoolz::GetURL()
|
|
|
|
{
|
2021-06-30 15:06:37 +08:00
|
|
|
return "https://github.com/Accelerator74/l4dtoolz";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool l4dtoolz::RegisterConCommandBase(ConCommandBase *pVar)
|
|
|
|
{
|
|
|
|
return META_REGCVAR(pVar);
|
2012-12-15 06:38:27 +08:00
|
|
|
}
|