1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-01-31 20:52:18 +01:00
HLMetaModOfficial/core/provider/source2/provider_source2.cpp

457 lines
13 KiB
C++

/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2023 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "provider_source2.h"
#include <metamod.h>
#include <metamod_util.h>
#include <metamod_console.h>
#include <amtl/am-string.h>
#include <eiface.h>
#include <KeyValues.h>
#include <filesystem.h>
#include <iserver.h>
static Source2Provider g_Source2Provider;
IMetamodSourceProvider* provider = &g_Source2Provider;
CON_COMMAND_EXTERN(meta, LocalCommand_Meta, "Metamod:Source control options");
static ISource2ServerConfig* serverconfig = NULL;
INetworkServerService* netservice = NULL;
IEngineServiceMgr* enginesvcmgr = NULL;
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CPlayerSlot, const CCommand&);
SH_DECL_HOOK3_void(IEngineServiceMgr, RegisterLoopMode, SH_NOATTRIB, 0, const char *, ILoopModeFactory *, void **);
SH_DECL_HOOK3_void(IEngineServiceMgr, UnregisterLoopMode, SH_NOATTRIB, 0, const char*, ILoopModeFactory*, void**);
SH_DECL_HOOK0(ILoopModeFactory, CreateLoopMode, SH_NOATTRIB, 0, ILoopMode *);
SH_DECL_HOOK1_void(ILoopModeFactory, DestroyLoopMode, SH_NOATTRIB, 0, ILoopMode *);
SH_DECL_HOOK2(ILoopMode, LoopInit, SH_NOATTRIB, 0, bool, KeyValues*, ILoopModePrerequisiteRegistry *);
SH_DECL_HOOK0_void(ILoopMode, LoopShutdown, SH_NOATTRIB, 0);
#ifdef SHOULD_OVERRIDE_ALLOWDEDICATED_SERVER
SH_DECL_HOOK1(ISource2ServerConfig, AllowDedicatedServers, const, 0, bool, EUniverse);
#endif
void Source2Provider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
CreateInterfaceFn serverFactory)
{
engine = (IVEngineServer*)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
if (!engine)
{
DisplayError("Could not find IVEngineServer! Metamod cannot load.");
return;
}
gpGlobals = engine->GetServerGlobals();
serverconfig = (ISource2ServerConfig*)((serverFactory)(INTERFACEVERSION_SERVERCONFIG, NULL));
netservice = (INetworkServerService*)((engineFactory)(NETWORKSERVERSERVICE_INTERFACE_VERSION, NULL));
enginesvcmgr = (IEngineServiceMgr*)((engineFactory)(ENGINESERVICEMGR_INTERFACE_VERSION, NULL));
icvar = (ICvar*)((engineFactory)(CVAR_INTERFACE_VERSION, NULL));
if (!icvar)
{
DisplayError("Could not find ICvar! Metamod cannot load.");
return;
}
gameclients = (IServerGameClients*)(serverFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL));
g_pFullFileSystem = baseFs = (IFileSystem*)((engineFactory)(FILESYSTEM_INTERFACE_VERSION, NULL));
if (baseFs == NULL)
{
mm_LogMessage("Unable to find \"%s\": .vdf files will not be parsed", FILESYSTEM_INTERFACE_VERSION);
}
// Since we have to be added as a Game path (cannot add GameBin directly), we
// automatically get added to other paths as well, including having the MM:S
// dir become the default write path for logs and more. We can fix some of these.
// GAMMACASE: This deals with the search paths where metamod could get added to, but there are still
// problems with this as console.log file for example would be created way earlier than we alter these search paths,
// and will be created in the metamod folder still, unsure what the solution to that could be tho!
// NOTE: baseFs->PrintSearchPaths(); could be used to print out search paths to debug them.
const char *pathIds[] = {
"ADDONS",
"CONTENT",
"CONTENTADDONS",
"CONTENTROOT",
"EXECUTABLE_PATH",
"GAME",
"GAMEBIN",
"GAMEROOT",
"MOD",
"PLATFORM",
"SHADER_SOURCE",
"SHADER_SOURCE_MOD",
"SHADER_SOURCE_ROOT"
};
for(size_t id = 0; id < (sizeof(pathIds) / sizeof(pathIds[0])); id++)
{
CUtlVector<CUtlString> searchPaths;
baseFs->GetSearchPathsForPathID(pathIds[id], (GetSearchPathTypes_t)0, searchPaths);
FOR_EACH_VEC(searchPaths, i)
{
if(strstr(searchPaths[i].Get(), "metamod") != 0)
{
baseFs->RemoveSearchPath(searchPaths[i].Get(), pathIds[id]);
}
}
}
baseFs->RemoveSearchPaths("DEFAULT_WRITE_PATH");
CBufferStringGrowable<260> searchPath;
baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, 1);
baseFs->AddSearchPath(searchPath.Get(), "DEFAULT_WRITE_PATH");
g_pCVar = icvar;
ConVar_Register(FCVAR_RELEASE);
if (gameclients)
{
SH_ADD_HOOK(IServerGameClients, ClientCommand, gameclients, SH_MEMBER(this, &Source2Provider::Hook_ClientCommand), false);
}
#ifdef SHOULD_OVERRIDE_ALLOWDEDICATED_SERVER
SH_ADD_VPHOOK(ISource2ServerConfig, AllowDedicatedServers, serverconfig, SH_MEMBER(this, &Source2Provider::Hook_AllowDedicatedServers), false);
#endif
SH_ADD_HOOK(IEngineServiceMgr, RegisterLoopMode, enginesvcmgr, SH_MEMBER(this, &Source2Provider::Hook_RegisterLoopMode), false);
SH_ADD_HOOK(IEngineServiceMgr, UnregisterLoopMode, enginesvcmgr, SH_MEMBER(this, &Source2Provider::Hook_UnregisterLoopMode), false);
}
void Source2Provider::Notify_DLLShutdown_Pre()
{
ConVar_Unregister();
SH_REMOVE_HOOK(IEngineServiceMgr, RegisterLoopMode, enginesvcmgr, SH_MEMBER(this, &Source2Provider::Hook_RegisterLoopMode), false);
SH_REMOVE_HOOK(IEngineServiceMgr, UnregisterLoopMode, enginesvcmgr, SH_MEMBER(this, &Source2Provider::Hook_UnregisterLoopMode), false);
if (gameclients)
{
SH_REMOVE_HOOK(IServerGameClients, ClientCommand, gameclients, SH_MEMBER(this, &Source2Provider::Hook_ClientCommand), false);
}
}
bool Source2Provider::ProcessVDF(const char* file, char path[], size_t path_len, char alias[], size_t alias_len)
{
if (baseFs == NULL)
{
return false;
}
KeyValues* pValues;
bool bKVLoaded = false;
const char* plugin_file, * p_alias;
pValues = new KeyValues("Metamod Plugin");
bKVLoaded = pValues->LoadFromFile(baseFs, file);
if (!bKVLoaded)
{
delete pValues;
return false;
}
if ((plugin_file = pValues->GetString("file", NULL)) == NULL)
{
delete pValues;
return false;
}
UTIL_Format(path, path_len, "%s", plugin_file);
if ((p_alias = pValues->GetString("alias", NULL)) != NULL)
{
UTIL_Format(alias, alias_len, "%s", p_alias);
}
else
{
UTIL_Format(alias, alias_len, "");
}
delete pValues;
return true;
}
int Source2Provider::DetermineSourceEngine()
{
#if SOURCE_ENGINE == SE_DOTA
return SOURCE_ENGINE_DOTA;
#elif SOURCE_ENGINE == SE_CS2
return SOURCE_ENGINE_CS2;
#else
#error "SOURCE_ENGINE not defined to a known value"
#endif
}
const char* Source2Provider::GetEngineDescription() const
{
#if SOURCE_ENGINE == SE_DOTA
return "Dota 2 (2013)";
#elif SOURCE_ENGINE == SE_CS2
return "Counter-Strike 2 (2023)";
#else
#error "SOURCE_ENGINE not defined to a known value"
#endif
}
void Source2Provider::GetGamePath(char* pszBuffer, int len)
{
CBufferStringGrowable<MAX_PATH> buf;
engine->GetGameDir(buf);
ke::SafeSprintf(pszBuffer, len, "%s", buf.Get());
}
const char* Source2Provider::GetGameDescription()
{
return serverconfig->GetGameDescription();
}
#ifdef SHOULD_OVERRIDE_ALLOWDEDICATED_SERVER
bool Source2Provider::Hook_AllowDedicatedServers(EUniverse universe) const
{
RETURN_META_VALUE(MRES_SUPERCEDE, true);
}
#endif
const char* Source2Provider::GetCommandLineValue(const char* key, const char* defval)
{
return CommandLine()->ParmValue(key, defval);
}
void Source2Provider::ConsolePrint(const char* str)
{
ConMsg("%s", str);
}
void Source2Provider::ClientConsolePrint(MMSPlayer_t client, const char* message)
{
engine->ClientPrintf(client, message);
}
void Source2Provider::ServerCommand(const char* cmd)
{
engine->ServerCommand(cmd);
}
const char* Source2Provider::GetConVarString(MetamodSourceConVar *convar)
{
#ifdef S2_CONVAR_UNFINISHED
if (convar == NULL)
{
return NULL;
}
return convar->GetString();
#else
return nullptr;
#endif
}
void Source2Provider::SetConVarString(MetamodSourceConVar *convar, const char* str)
{
#ifdef S2_CONVAR_UNFINISHED
convar->SetValue(str);
#endif
}
bool Source2Provider::IsConCommandBaseACommand(ConCommandBase* pCommand)
{
#ifdef S2_CONVAR_UNFINISHED
return pCommand->IsCommand();
#else
return false;
#endif
}
bool Source2Provider::RegisterConCommandBase(ConCommandBase* pCommand)
{
#ifdef S2_CONVAR_UNFINISHED
return g_SMConVarAccessor.Register(pCommand);
#else
return true;
#endif
}
void Source2Provider::UnregisterConCommandBase(ConCommandBase* pCommand)
{
#ifdef S2_CONVAR_UNFINISHED
return g_SMConVarAccessor.Unregister(pCommand);
#endif
}
MetamodSourceConVar* Source2Provider::CreateConVar(const char* name,
const char* defval,
const char* help,
int flags)
{
#ifdef S2_CONVAR_UNFINISHED
int newflags = 0;
if (flags & ConVarFlag_Notify)
{
newflags |= FCVAR_NOTIFY;
}
if (flags & ConVarFlag_SpOnly)
{
newflags |= FCVAR_SPONLY;
}
ConVar* pVar = new ConVar(name, defval, newflags, help);
g_SMConVarAccessor.RegisterConCommandBase(pVar);
return pVar;
#else
return nullptr;
#endif
}
class GlobCommand : public IMetamodSourceCommandInfo
{
public:
GlobCommand(const CCommand* cmd) : m_cmd(cmd)
{
}
public:
unsigned int GetArgCount()
{
return m_cmd->ArgC() - 1;
}
const char* GetArg(unsigned int num)
{
return m_cmd->Arg(num);
}
const char* GetArgString()
{
return m_cmd->ArgS();
}
private:
const CCommand* m_cmd;
};
void LocalCommand_Meta(const CCommandContext &, const CCommand& args)
{
if (nullptr != g_Source2Provider.m_pCallbacks)
{
GlobCommand cmd(&args);
g_Source2Provider.m_pCallbacks->OnCommand_Meta(&cmd);
}
}
void Source2Provider::Hook_RegisterLoopMode(const char *pszLoopModeName, ILoopModeFactory *pLoopModeFactory, void **ppGlobalPointer)
{
if (!strcmp(pszLoopModeName, "game"))
{
SH_ADD_HOOK(ILoopModeFactory, CreateLoopMode, pLoopModeFactory, SH_MEMBER(this, &Source2Provider::Hook_CreateLoopModePost), true);
SH_ADD_HOOK(ILoopModeFactory, DestroyLoopMode, pLoopModeFactory, SH_MEMBER(this, &Source2Provider::Hook_DestroyLoopMode), false);
if (nullptr != m_pCallbacks)
{
m_pCallbacks->OnGameInit();
}
}
}
void Source2Provider::Hook_UnregisterLoopMode(const char* pszLoopModeName, ILoopModeFactory* pLoopModeFactory, void** ppGlobalPointer)
{
if (!strcmp(pszLoopModeName, "game"))
{
SH_REMOVE_HOOK(ILoopModeFactory, CreateLoopMode, pLoopModeFactory, SH_MEMBER(this, &Source2Provider::Hook_CreateLoopModePost), true);
SH_REMOVE_HOOK(ILoopModeFactory, DestroyLoopMode, pLoopModeFactory, SH_MEMBER(this, &Source2Provider::Hook_DestroyLoopMode), false);
}
RETURN_META(MRES_IGNORED);
}
ILoopMode *Source2Provider::Hook_CreateLoopModePost()
{
ILoopMode *pLoopMode = META_RESULT_ORIG_RET(ILoopMode *);
SH_ADD_HOOK(ILoopMode, LoopInit, pLoopMode, SH_MEMBER(this, &Source2Provider::Hook_LoopInitPost), true);
SH_ADD_HOOK(ILoopMode, LoopShutdown, pLoopMode, SH_MEMBER(this, &Source2Provider::Hook_LoopShutdownPost), true);
// Post-hook. Ignored
return nullptr;
}
void Source2Provider::Hook_DestroyLoopMode(ILoopMode* pLoopMode)
{
SH_REMOVE_HOOK(ILoopMode, LoopInit, pLoopMode, SH_MEMBER(this, &Source2Provider::Hook_LoopInitPost), true);
SH_REMOVE_HOOK(ILoopMode, LoopShutdown, pLoopMode, SH_MEMBER(this, &Source2Provider::Hook_LoopShutdownPost), true);
}
bool Source2Provider::Hook_LoopInitPost(KeyValues* pKeyValues, ILoopModePrerequisiteRegistry *pRegistry)
{
if (nullptr != m_pCallbacks)
{
m_pCallbacks->OnLevelInit(
pKeyValues->GetString("levelname"),
"",
pKeyValues->GetString("previouslevel"),
pKeyValues->GetString("landmarkname"),
pKeyValues->GetBool("loadmap"),
false
);
}
// Post-hook. Ignored
return true;
}
void Source2Provider::Hook_LoopShutdownPost()
{
if (nullptr != m_pCallbacks)
{
m_pCallbacks->OnLevelShutdown();
}
}
void Source2Provider::Hook_ClientCommand(CPlayerSlot nSlot, const CCommand& _cmd)
{
GlobCommand cmd(&_cmd);
if (strcmp(cmd.GetArg(0), "meta") == 0)
{
if (nullptr != m_pCallbacks)
{
m_pCallbacks->OnCommand_ClientMeta(nSlot, &cmd);
}
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}