mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-01-19 08:52:34 +01:00
Round 1 of base provider split. (Only compilation has been tested)
This commit is contained in:
parent
3704e2f3c1
commit
e5f5a8ce6f
323
core/metamod.cpp
323
core/metamod.cpp
@ -35,9 +35,6 @@
|
||||
#include "metamod_console.h"
|
||||
#include "provider/provider_base.h"
|
||||
#include <sys/stat.h>
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
#include <iserver.h>
|
||||
#endif
|
||||
|
||||
#define X64_SUFFIX ".x64"
|
||||
|
||||
@ -50,50 +47,12 @@ using namespace SourceHook::Impl;
|
||||
* @file sourcemm.cpp
|
||||
*/
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
// Hack to make hook decl compile when only having forward decl in header.
|
||||
// (we have class structure but it requires protobuf which we don't want to include here)
|
||||
class GameSessionConfiguration_t { };
|
||||
|
||||
SH_DECL_MANUALHOOK3_void(SGD_StartupServer, 0, 0, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *);
|
||||
SH_DECL_MANUALHOOK2_void(SGD_Init, 0, 0, 0, GameSessionConfiguration_t *, const char *);
|
||||
SH_DECL_MANUALHOOK3(SGD_StartChangeLevel, 0, 0, 0, CUtlVector<INetworkGameClient *> *, const char *, const char *, void *);
|
||||
SH_DECL_MANUALHOOK5_void(SGD_SwitchToLoop, 0, 0, 0, const char *, KeyValues *, uint32, const char *, bool);
|
||||
|
||||
static void
|
||||
Handler_SwitchToLoop(const char *, KeyValues *, uint32, const char *, bool);
|
||||
|
||||
static void
|
||||
Handler_StartupServer_Post(const GameSessionConfiguration_t &, ISource2WorldSession *, const char *);
|
||||
|
||||
static void
|
||||
Handler_Init(GameSessionConfiguration_t *, const char *);
|
||||
|
||||
static CUtlVector<INetworkGameClient *> *
|
||||
Handler_StartChangeLevel(const char *, const char *, void *);
|
||||
#else
|
||||
SH_DECL_MANUALHOOK0(SGD_GameInit, 0, 0, 0, bool);
|
||||
SH_DECL_MANUALHOOK6(SGD_LevelInit, 0, 0, 0, bool, const char *, const char *, const char *, const char *, bool, bool);
|
||||
SH_DECL_MANUALHOOK0_void(SGD_LevelShutdown, 0, 0, 0);
|
||||
|
||||
static void
|
||||
Handler_LevelShutdown();
|
||||
|
||||
static bool
|
||||
Handler_LevelInit(char const *pMapName,
|
||||
char const *pMapEntities,
|
||||
char const *pOldLevel,
|
||||
char const *pLandmarkName,
|
||||
bool loadGame,
|
||||
bool background);
|
||||
|
||||
static bool
|
||||
Handler_GameInit();
|
||||
#endif
|
||||
|
||||
static void
|
||||
InitializeVSP();
|
||||
|
||||
static void
|
||||
DoInitialPluginLoads();
|
||||
|
||||
static int
|
||||
LoadPluginsFromFile(const char *filepath, int &skipped);
|
||||
|
||||
@ -170,6 +129,73 @@ SourceMM::ISmmAPI *g_pMetamod = &g_Metamod;
|
||||
} \
|
||||
}
|
||||
|
||||
static class ProviderCallbacks : public IMetamodSourceProviderCallbacks
|
||||
{
|
||||
virtual void OnGameInit() override
|
||||
{
|
||||
if (is_game_init)
|
||||
return;
|
||||
|
||||
provider->DisplayDevMsg("MMS: OnGameInit\n");
|
||||
|
||||
if (vsp_load_requested)
|
||||
InitializeVSP();
|
||||
|
||||
if (g_bIsVspBridged && !were_plugins_loaded)
|
||||
{
|
||||
DoInitialPluginLoads();
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
were_plugins_loaded = true;
|
||||
}
|
||||
|
||||
is_game_init = true;
|
||||
}
|
||||
|
||||
virtual void OnLevelInit(char const* pMapName, char const* pMapEntities, char const* pOldLevel,
|
||||
char const* pLandmarkName, bool loadGame, bool background) override
|
||||
{
|
||||
provider->DisplayDevMsg("MMS: LevelInit\n");
|
||||
|
||||
ITER_EVENT(OnLevelInit, (pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background));
|
||||
}
|
||||
|
||||
virtual void OnLevelShutdown() override
|
||||
{
|
||||
provider->DisplayDevMsg("MMS: LevelShutdown\n");
|
||||
|
||||
if (g_bIsVspBridged && !were_plugins_loaded)
|
||||
{
|
||||
DoInitialPluginLoads();
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
were_plugins_loaded = true;
|
||||
in_first_level = true;
|
||||
}
|
||||
|
||||
if (!in_first_level)
|
||||
{
|
||||
char filepath[PATH_SIZE], vdfpath[PATH_SIZE];
|
||||
|
||||
g_Metamod.PathFormat(filepath,
|
||||
sizeof(filepath),
|
||||
"%s/%s",
|
||||
mod_path.c_str(),
|
||||
provider->GetConVarString(mm_pluginsfile));
|
||||
g_Metamod.PathFormat(vdfpath,
|
||||
sizeof(vdfpath),
|
||||
"%s/%s",
|
||||
mod_path.c_str(),
|
||||
provider->GetConVarString(mm_basedir));
|
||||
mm_LoadPlugins(filepath, vdfpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
in_first_level = false;
|
||||
}
|
||||
|
||||
ITER_EVENT(OnLevelShutdown, ());
|
||||
}
|
||||
} s_ProviderCallbacks;
|
||||
|
||||
/* Initialize everything here */
|
||||
void
|
||||
mm_InitializeForLoad()
|
||||
@ -184,46 +210,7 @@ mm_InitializeForLoad()
|
||||
*/
|
||||
in_first_level = true;
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
SourceHook::MemFuncInfo info;
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_StartupServer, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for INetworkServerService::StartupServer");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_StartupServer, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALHOOK(SGD_StartupServer, netservice, SH_STATIC(Handler_StartupServer_Post), true);
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_SwitchToLoop, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for IEngineServiceMgr::SwitchToLoop");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_SwitchToLoop, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALHOOK(SGD_SwitchToLoop, enginesvcmgr, SH_STATIC(Handler_SwitchToLoop), false);
|
||||
#else
|
||||
SourceHook::MemFuncInfo info;
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_GameInit, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for IServerGameDLL::GameInit");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_GameInit, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALHOOK_STATICFUNC(SGD_GameInit, server, Handler_GameInit, false);
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_LevelInit, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for IServerGameDLL::LevelInit");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_LevelInit, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALHOOK_STATICFUNC(SGD_LevelInit, server, Handler_LevelInit, true);
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_LevelShutdown, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for IServerGameDLL::LevelShutdown");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_LevelShutdown, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALHOOK_STATICFUNC(SGD_LevelShutdown, server, Handler_LevelShutdown, true);
|
||||
#endif
|
||||
provider->SetCallbacks(&s_ProviderCallbacks);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -531,174 +518,6 @@ mm_UnloadMetamod()
|
||||
g_SourceHook.CompleteShutdown();
|
||||
}
|
||||
|
||||
static void
|
||||
mm_HandleGameInit()
|
||||
{
|
||||
if (is_game_init)
|
||||
return;
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
DevMsg("MMS: GameInit\n");
|
||||
#endif
|
||||
|
||||
if (vsp_load_requested)
|
||||
InitializeVSP();
|
||||
|
||||
if (g_bIsVspBridged && !were_plugins_loaded)
|
||||
{
|
||||
DoInitialPluginLoads();
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
were_plugins_loaded = true;
|
||||
}
|
||||
|
||||
is_game_init = true;
|
||||
}
|
||||
|
||||
static void
|
||||
mm_HandleLevelShutdown()
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
DevMsg("MMS: LevelShutdown\n");
|
||||
#endif
|
||||
|
||||
if (g_bIsVspBridged && !were_plugins_loaded)
|
||||
{
|
||||
DoInitialPluginLoads();
|
||||
g_PluginMngr.SetAllLoaded();
|
||||
were_plugins_loaded = true;
|
||||
in_first_level = true;
|
||||
}
|
||||
|
||||
if (!in_first_level)
|
||||
{
|
||||
char filepath[PATH_SIZE], vdfpath[PATH_SIZE];
|
||||
|
||||
g_Metamod.PathFormat(filepath,
|
||||
sizeof(filepath),
|
||||
"%s/%s",
|
||||
mod_path.c_str(),
|
||||
provider->GetConVarString(mm_pluginsfile));
|
||||
g_Metamod.PathFormat(vdfpath,
|
||||
sizeof(vdfpath),
|
||||
"%s/%s",
|
||||
mod_path.c_str(),
|
||||
provider->GetConVarString(mm_basedir));
|
||||
mm_LoadPlugins(filepath, vdfpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
in_first_level = false;
|
||||
}
|
||||
|
||||
ITER_EVENT(OnLevelShutdown, ());
|
||||
}
|
||||
|
||||
static void
|
||||
mm_HandleLevelInit(char const *pMapName,
|
||||
char const *pMapEntities,
|
||||
char const *pOldLevel,
|
||||
char const *pLandmarkName,
|
||||
bool loadGame,
|
||||
bool background)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
DevMsg("MMS: LevelInit\n");
|
||||
#endif
|
||||
|
||||
ITER_EVENT(OnLevelInit, (pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background));
|
||||
}
|
||||
#include <utlbuffer.h>
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
static void
|
||||
Handler_SwitchToLoop(const char *pszLoopName, KeyValues *pKV, uint32 nId, const char *pszUnk, bool bUnk)
|
||||
{
|
||||
if (strcmp(pszLoopName, "levelload") == 0)
|
||||
{
|
||||
mm_HandleGameInit();
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static void
|
||||
Handler_StartupServer_Post(const GameSessionConfiguration_t &config, ISource2WorldSession *, const char *)
|
||||
{
|
||||
static bool bGameServerHooked = false;
|
||||
if (!bGameServerHooked)
|
||||
{
|
||||
INetworkGameServer *netserver = (META_IFACEPTR(INetworkServerService))->GetIGameServer();
|
||||
|
||||
SourceHook::MemFuncInfo info;
|
||||
if (!provider->GetHookInfo(ProvidedHook_Init, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for INetworkGameServer::Init");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_Init, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALVPHOOK(SGD_Init, netserver, SH_STATIC(Handler_Init), false);
|
||||
|
||||
if (!provider->GetHookInfo(ProvidedHook_StartChangeLevel, &info))
|
||||
{
|
||||
provider->DisplayError("Metamod:Source could not find a valid hook for INetworkGameServer::StartChangeLevel");
|
||||
}
|
||||
SH_MANUALHOOK_RECONFIGURE(SGD_StartChangeLevel, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
||||
SH_ADD_MANUALVPHOOK(SGD_StartChangeLevel, netserver, SH_STATIC(Handler_StartChangeLevel), false);
|
||||
|
||||
bGameServerHooked = true;
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static void
|
||||
Handler_Init(GameSessionConfiguration_t *pConfig, const char *pszMapName)
|
||||
{
|
||||
static char szLastMap[260] = "";
|
||||
mm_HandleLevelInit(pszMapName, "", szLastMap, "", false, false);
|
||||
UTIL_Format(szLastMap, sizeof(szLastMap), "%s", pszMapName);
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static CUtlVector<INetworkGameClient *> *
|
||||
Handler_StartChangeLevel(const char *, const char *, void *)
|
||||
{
|
||||
mm_HandleLevelShutdown();
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool
|
||||
Handler_GameInit()
|
||||
{
|
||||
mm_HandleGameInit();
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
static void
|
||||
Handler_LevelShutdown(void)
|
||||
{
|
||||
mm_HandleLevelShutdown();
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static bool
|
||||
Handler_LevelInit(char const *pMapName,
|
||||
char const *pMapEntities,
|
||||
char const *pOldLevel,
|
||||
char const *pLandmarkName,
|
||||
bool loadGame,
|
||||
bool background)
|
||||
{
|
||||
ITER_EVENT(OnLevelInit, (pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background));
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
void MetamodSource::LogMsg(ISmmPlugin *pl, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
@ -49,6 +49,29 @@ namespace SourceMM
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Interface for Metamod:Source to provide callbacks to the
|
||||
* provider.
|
||||
*/
|
||||
class IMetamodSourceProviderCallbacks
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Called before the server DLL handles game initialization.
|
||||
*/
|
||||
virtual void OnGameInit() = 0;
|
||||
|
||||
/**
|
||||
* @brief Called after the server DLL has completed handling level/map initialization.
|
||||
*/
|
||||
virtual void OnLevelInit(char const* pMapName, char const* pMapEntities, char const* pOldLevel, char const* pLandmarkName, bool loadGame, bool background) = 0;
|
||||
|
||||
/**
|
||||
* @brief Called after the server DLL has completed handling level/map shut down.
|
||||
*/
|
||||
virtual void OnLevelShutdown() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Abstracts command information, since the new engine fixes the
|
||||
* re-entrancy problems in the tokenization system.
|
||||
@ -83,6 +106,13 @@ namespace SourceMM
|
||||
class IMetamodSourceProvider
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Set the callback interface for the provider to call into.
|
||||
*
|
||||
* @param pCallbacks Pointer to callback interface implementation.
|
||||
*/
|
||||
virtual void SetCallbacks(IMetamodSourceProviderCallbacks* pCallbacks) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether source engine build is compatible.
|
||||
*
|
||||
@ -91,18 +121,6 @@ namespace SourceMM
|
||||
*/
|
||||
virtual bool IsSourceEngineBuildCompatible(int build) =0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves hook information for each callback. Each hook
|
||||
* must be implemented.
|
||||
*
|
||||
* @param hook Hook information to provide.
|
||||
* @param pInfo Non-NULL pointer to fill with information
|
||||
* about the hook's virtual location.
|
||||
* @return True if supported, false to fail, which
|
||||
* will cause Metamod:Source to fail.
|
||||
*/
|
||||
virtual bool GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo) =0;
|
||||
|
||||
/**
|
||||
* @brief Logs a message via IVEngineServer::LogPrint.
|
||||
*
|
||||
@ -167,6 +185,16 @@ namespace SourceMM
|
||||
*/
|
||||
virtual void DisplayWarning(const char *fmt, ...) =0;
|
||||
|
||||
/**
|
||||
* @brief Sends the server a developer message.
|
||||
*
|
||||
* No newline is appended.
|
||||
*
|
||||
* @param fmt Formatted message string.
|
||||
* @param ... Format parameters.
|
||||
*/
|
||||
virtual void DisplayDevMsg(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Attempts to notify the provider of the gamedll version being
|
||||
* used.
|
||||
|
@ -2,7 +2,7 @@
|
||||
* vim: set ts=4 sw=4 tw=99 noet :
|
||||
* ======================================================
|
||||
* Metamod:Source
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC and authors.
|
||||
* Copyright (C) 2004-2023 AlliedModders LLC and authors.
|
||||
* All rights reserved.
|
||||
* ======================================================
|
||||
*
|
||||
@ -30,8 +30,6 @@
|
||||
#include <convar.h>
|
||||
#include <eiface.h>
|
||||
#include <tier0/icommandline.h>
|
||||
#include <sh_vector.h>
|
||||
#include <sh_string.h>
|
||||
#include "../metamod_util.h"
|
||||
#include "provider_base.h"
|
||||
#include "console.h"
|
||||
@ -39,31 +37,7 @@
|
||||
#include <filesystem.h>
|
||||
#include "metamod.h"
|
||||
#include <tier1/KeyValues.h>
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
#include <iserver.h>
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 )
|
||||
SH_DECL_HOOK1(ISource2ServerConfig, AllowDedicatedServers, const, 0, bool, EUniverse);
|
||||
bool BaseProvider::AllowDedicatedServers(EUniverse universe) const
|
||||
{
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Types */
|
||||
typedef void (*CONPRINTF_FUNC)(const char *, ...);
|
||||
struct UsrMsgInfo
|
||||
{
|
||||
UsrMsgInfo()
|
||||
{
|
||||
}
|
||||
UsrMsgInfo(int s, const char *t) : size(s), name(t)
|
||||
{
|
||||
}
|
||||
int size;
|
||||
String name;
|
||||
};
|
||||
|
||||
/* Imports */
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
@ -71,239 +45,21 @@ struct UsrMsgInfo
|
||||
DLL_IMPORT ICommandLine *CommandLine();
|
||||
#endif
|
||||
|
||||
/* Functions */
|
||||
void CacheUserMessages();
|
||||
bool KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL);
|
||||
void Detour_Error(const tchar *pMsg, ...);
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
void ClientCommand(CEntityIndex index, const CCommand &args);
|
||||
#elif SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void ClientCommand(edict_t *pEdict, const CCommand &args);
|
||||
#else
|
||||
void ClientCommand(edict_t *pEdict);
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void LocalCommand_Meta(const CCommand &args);
|
||||
#else
|
||||
void LocalCommand_Meta();
|
||||
#endif
|
||||
|
||||
void _ServerCommand();
|
||||
/* Variables */
|
||||
static BaseProvider g_Ep1Provider;
|
||||
static List<ConCommandBase *> conbases_unreg;
|
||||
static CVector<UsrMsgInfo> usermsgs_list;
|
||||
static jmp_buf usermsg_end;
|
||||
static bool g_bOriginalEngine = false;
|
||||
|
||||
ICvar *icvar = NULL;
|
||||
IFileSystem *baseFs = NULL;
|
||||
IServerGameDLL *server = NULL;
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
static ISource2ServerConfig *serverconfig = NULL;
|
||||
INetworkServerService *netservice = NULL;
|
||||
IEngineServiceMgr *enginesvcmgr = NULL;
|
||||
#endif
|
||||
IVEngineServer *engine = NULL;
|
||||
IServerGameClients *gameclients = NULL;
|
||||
CGlobalVars *gpGlobals = NULL;
|
||||
|
||||
ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options");
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CEntityIndex, const CCommand &);
|
||||
#elif SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
|
||||
#else
|
||||
SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *);
|
||||
#endif
|
||||
|
||||
void BaseProvider::ConsolePrint(const char *str)
|
||||
{
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
ConMsg("%s", str);
|
||||
#else
|
||||
Msg("%s", str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
|
||||
CreateInterfaceFn serverFactory)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_SDK2013
|
||||
// Shim to avoid hooking shims
|
||||
engine = (IVEngineServer *)((engineFactory)("VEngineServer023", NULL));
|
||||
if (!engine)
|
||||
{
|
||||
engine = (IVEngineServer *)((engineFactory)("VEngineServer022", NULL));
|
||||
if (!engine)
|
||||
{
|
||||
engine = (IVEngineServer *)((engineFactory)("VEngineServer021", NULL));
|
||||
}
|
||||
}
|
||||
#else
|
||||
engine = (IVEngineServer *)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
|
||||
#endif
|
||||
if (!engine)
|
||||
{
|
||||
DisplayError("Could not find IVEngineServer! Metamod cannot load.");
|
||||
return;
|
||||
}
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
gpGlobals = engine->GetServerGlobals();
|
||||
serverconfig = (ISource2ServerConfig *) ((serverFactory) (INTERFACEVERSION_SERVERCONFIG, NULL));
|
||||
netservice = (INetworkServerService *) ((engineFactory) (NETWORKSERVERSERVICE_INTERFACE_VERSION, NULL));
|
||||
enginesvcmgr = (IEngineServiceMgr *) ((engineFactory) (ENGINESERVICEMGR_INTERFACE_VERSION, NULL));
|
||||
#endif
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
icvar = (ICvar *)((engineFactory)(CVAR_INTERFACE_VERSION, NULL));
|
||||
#else
|
||||
icvar = (ICvar *)((engineFactory)(VENGINE_CVAR_INTERFACE_VERSION, NULL));
|
||||
#endif
|
||||
if (!icvar)
|
||||
{
|
||||
DisplayError("Could not find ICvar! Metamod cannot load.");
|
||||
return;
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
gameclients = (IServerGameClients *)(serverFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL));
|
||||
#else
|
||||
if ((gameclients = (IServerGameClients *)(serverFactory("ServerGameClients003", NULL)))
|
||||
== NULL)
|
||||
{
|
||||
gameclients = (IServerGameClients *)(serverFactory("ServerGameClients004", NULL));
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA && 0
|
||||
// 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.
|
||||
|
||||
char searchPath[260];
|
||||
baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath));
|
||||
for (size_t i = 0; i < sizeof(searchPath); ++i)
|
||||
{
|
||||
if (searchPath[i] == ';')
|
||||
{
|
||||
searchPath[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
baseFs->RemoveSearchPath(searchPath, "GAME");
|
||||
|
||||
// TODO: figure out why these calls get ignored and path remains
|
||||
//baseFs->RemoveSearchPath(searchPath, "CONTENT");
|
||||
//baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE");
|
||||
//baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE_MOD");
|
||||
|
||||
baseFs->RemoveSearchPaths("DEFAULT_WRITE_PATH");
|
||||
baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath));
|
||||
for (size_t i = 0; i < sizeof(searchPath); ++i)
|
||||
{
|
||||
if (searchPath[i] == ';')
|
||||
{
|
||||
searchPath[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
baseFs->AddSearchPath(searchPath, "DEFAULT_WRITE_PATH");
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
g_pCVar = icvar;
|
||||
#endif
|
||||
|
||||
g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd);
|
||||
|
||||
#if SOURCE_ENGINE == SE_EPISODEONE
|
||||
/* The Ship is the only game known at this time that uses the pre-Episode One engine */
|
||||
g_bOriginalEngine = strcmp(CommandLine()->ParmValue("-game", "hl2"), "ship") == 0;
|
||||
#endif
|
||||
|
||||
CacheUserMessages();
|
||||
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
if (!g_SMConVarAccessor.InitConCommandBaseList())
|
||||
{
|
||||
/* This is very unlikely considering it's old engine */
|
||||
mm_LogMessage("[META] Warning: Failed to find ConCommandBase list!");
|
||||
mm_LogMessage("[META] Warning: ConVars and ConCommands cannot be unregistered properly! Please file a bug report.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gameclients)
|
||||
{
|
||||
SH_ADD_HOOK_STATICFUNC(IServerGameClients, ClientCommand, gameclients, ClientCommand, false);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 )
|
||||
SH_ADD_VPHOOK(ISource2ServerConfig, AllowDedicatedServers, serverconfig, SH_MEMBER(this, &BaseProvider::AllowDedicatedServers), false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseProvider::Notify_DLLShutdown_Pre()
|
||||
{
|
||||
g_SMConVarAccessor.RemoveMetamodCommands();
|
||||
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
if (g_Metamod.IsLoadedAsGameDLL())
|
||||
{
|
||||
icvar->UnlinkVariables(FCVAR_GAMEDLL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BaseProvider::IsRemotePrintingAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseProvider::ClientConsolePrint(edict_t *pEdict, const char *message)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
int client = (int)(pEdict - gpGlobals->pEdicts);
|
||||
engine->ClientPrintf(client, message);
|
||||
#else
|
||||
engine->ClientPrintf(pEdict, message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseProvider::ServerCommand(const char *cmd)
|
||||
{
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
|
||||
const char *BaseProvider::GetConVarString(ConVar *convar)
|
||||
{
|
||||
if (convar == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return convar->GetString();
|
||||
}
|
||||
|
||||
void BaseProvider::SetConVarString(ConVar *convar, const char *str)
|
||||
{
|
||||
convar->SetValue(str);
|
||||
}
|
||||
|
||||
bool BaseProvider::IsConCommandBaseACommand(ConCommandBase *pCommand)
|
||||
{
|
||||
return pCommand->IsCommand();
|
||||
}
|
||||
|
||||
|
||||
bool BaseProvider::IsSourceEngineBuildCompatible(int build)
|
||||
{
|
||||
return (build == SOURCE_ENGINE_ORIGINAL
|
||||
@ -352,54 +108,6 @@ bool BaseProvider::LogMessage(const char *buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseProvider::GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
SourceHook::MemFuncInfo mfi = {true, -1, 0, 0};
|
||||
|
||||
switch (hook)
|
||||
{
|
||||
case ProvidedHook_StartupServer:
|
||||
SourceHook::GetFuncInfo(&INetworkServerService::StartupServer, mfi);
|
||||
break;
|
||||
case ProvidedHook_StartChangeLevel:
|
||||
SourceHook::GetFuncInfo(&INetworkGameServer::StartChangeLevel, mfi);
|
||||
break;
|
||||
case ProvidedHook_Init:
|
||||
SourceHook::GetFuncInfo(&INetworkGameServer::Init, mfi);
|
||||
break;
|
||||
case ProvidedHook_SwitchToLoop:
|
||||
SourceHook::GetFuncInfo(&IEngineServiceMgr::SwitchToLoop, mfi);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
*pInfo = mfi;
|
||||
|
||||
return (mfi.thisptroffs >= 0);
|
||||
#else
|
||||
SourceHook::MemFuncInfo mfi = {true, -1, 0, 0};
|
||||
|
||||
if (hook == ProvidedHook_LevelInit)
|
||||
{
|
||||
SourceHook::GetFuncInfo(&IServerGameDLL::LevelInit, mfi);
|
||||
}
|
||||
else if (hook == ProvidedHook_LevelShutdown)
|
||||
{
|
||||
SourceHook::GetFuncInfo(&IServerGameDLL::LevelShutdown, mfi);
|
||||
}
|
||||
else if (hook == ProvidedHook_GameInit)
|
||||
{
|
||||
SourceHook::GetFuncInfo(&IServerGameDLL::GameInit, mfi);
|
||||
}
|
||||
|
||||
*pInfo = mfi;
|
||||
|
||||
return (mfi.thisptroffs >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseProvider::DisplayError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
@ -424,444 +132,14 @@ void BaseProvider::DisplayWarning(const char *fmt, ...)
|
||||
Warning("%s", buffer);
|
||||
}
|
||||
|
||||
IConCommandBaseAccessor *BaseProvider::GetConCommandBaseAccessor()
|
||||
void BaseProvider::DisplayDevMsg(const char* fmt, ...)
|
||||
{
|
||||
return &g_SMConVarAccessor;
|
||||
}
|
||||
|
||||
bool BaseProvider::RegisterConCommandBase(ConCommandBase *pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Register(pCommand);
|
||||
}
|
||||
|
||||
void BaseProvider::UnregisterConCommandBase(ConCommandBase *pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Unregister(pCommand);
|
||||
}
|
||||
|
||||
int BaseProvider::GetUserMessageCount()
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
|
||||
return -1;
|
||||
#else
|
||||
return (int)usermsgs_list.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
int BaseProvider::FindUserMessage(const char *name, int *size)
|
||||
{
|
||||
for (size_t i = 0; i < usermsgs_list.size(); i++)
|
||||
{
|
||||
if (usermsgs_list[i].name.compare(name) == 0)
|
||||
{
|
||||
if (size)
|
||||
{
|
||||
*size = usermsgs_list[i].size;
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *BaseProvider::GetUserMessage(int index, int *size)
|
||||
{
|
||||
if (index < 0 || index >= (int)usermsgs_list.size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size)
|
||||
{
|
||||
*size = usermsgs_list[index].size;
|
||||
}
|
||||
|
||||
return usermsgs_list[index].name.c_str();
|
||||
}
|
||||
|
||||
void BaseProvider::GetGamePath(char *pszBuffer, int len)
|
||||
{
|
||||
engine->GetGameDir(pszBuffer, len);
|
||||
}
|
||||
|
||||
const char *BaseProvider::GetGameDescription()
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
return serverconfig->GetGameDescription();
|
||||
#else
|
||||
return server->GetGameDescription();
|
||||
#endif
|
||||
}
|
||||
|
||||
int BaseProvider::DetermineSourceEngine()
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_BLOODYGOODTIME
|
||||
return SOURCE_ENGINE_BLOODYGOODTIME;
|
||||
#elif SOURCE_ENGINE == SE_ALIENSWARM
|
||||
return SOURCE_ENGINE_ALIENSWARM;
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
|
||||
return SOURCE_ENGINE_LEFT4DEAD2;
|
||||
#elif SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
return SOURCE_ENGINE_NUCLEARDAWN;
|
||||
#elif SOURCE_ENGINE == SE_CONTAGION
|
||||
return SOURCE_ENGINE_CONTAGION;
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD
|
||||
return SOURCE_ENGINE_LEFT4DEAD;
|
||||
#elif SOURCE_ENGINE == SE_ORANGEBOX
|
||||
return SOURCE_ENGINE_ORANGEBOX;
|
||||
#elif SOURCE_ENGINE == SE_CSS
|
||||
return SOURCE_ENGINE_CSS;
|
||||
#elif SOURCE_ENGINE == SE_HL2DM
|
||||
return SOURCE_ENGINE_HL2DM;
|
||||
#elif SOURCE_ENGINE == SE_DODS
|
||||
return SOURCE_ENGINE_DODS;
|
||||
#elif SOURCE_ENGINE == SE_SDK2013
|
||||
return SOURCE_ENGINE_SDK2013;
|
||||
#elif SOURCE_ENGINE == SE_TF2
|
||||
return SOURCE_ENGINE_TF2;
|
||||
#elif SOURCE_ENGINE == SE_DARKMESSIAH
|
||||
return SOURCE_ENGINE_DARKMESSIAH;
|
||||
#elif SOURCE_ENGINE == SE_EYE
|
||||
return SOURCE_ENGINE_EYE;
|
||||
#elif SOURCE_ENGINE == SE_PORTAL2
|
||||
return SOURCE_ENGINE_PORTAL2;
|
||||
#elif SOURCE_ENGINE == SE_BLADE
|
||||
return SOURCE_ENGINE_BLADE;
|
||||
#elif SOURCE_ENGINE == SE_INSURGENCY
|
||||
return SOURCE_ENGINE_INSURGENCY;
|
||||
#elif SOURCE_ENGINE == SE_DOI
|
||||
return SOURCE_ENGINE_DOI;
|
||||
#elif SOURCE_ENGINE == SE_CSGO
|
||||
return SOURCE_ENGINE_CSGO;
|
||||
#elif SOURCE_ENGINE == SE_DOTA
|
||||
return SOURCE_ENGINE_DOTA;
|
||||
#elif SOURCE_ENGINE == SE_BMS
|
||||
return SOURCE_ENGINE_BMS;
|
||||
#elif SOURCE_ENGINE == SE_EPISODEONE
|
||||
return g_bOriginalEngine ? SOURCE_ENGINE_ORIGINAL : SOURCE_ENGINE_EPISODEONE;
|
||||
#elif SOURCE_ENGINE == SE_MOCK
|
||||
return SOURCE_ENGINE_MOCK;
|
||||
#elif SOURCE_ENGINE == SE_PVKII
|
||||
return SOURCE_ENGINE_PVKII;
|
||||
#elif SOURCE_ENGINE == SE_MCV
|
||||
return SOURCE_ENGINE_MCV;
|
||||
#else
|
||||
#error "SOURCE_ENGINE not defined to a known value"
|
||||
#endif
|
||||
}
|
||||
|
||||
ConVar *BaseProvider::CreateConVar(const char *name,
|
||||
const char *defval,
|
||||
const char *help,
|
||||
int flags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool BaseProvider::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");
|
||||
|
||||
if (g_bOriginalEngine)
|
||||
{
|
||||
/* The Ship must use a special version of this function */
|
||||
bKVLoaded = KVLoadFromFile(pValues, baseFs, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
bKVLoaded = pValues->LoadFromFile(baseFs, file);
|
||||
}
|
||||
|
||||
if (!bKVLoaded)
|
||||
{
|
||||
pValues->deleteThis();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((plugin_file = pValues->GetString("file", NULL)) == NULL)
|
||||
{
|
||||
pValues->deleteThis();
|
||||
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, "");
|
||||
}
|
||||
|
||||
pValues->deleteThis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *BaseProvider::GetEngineDescription() const
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_BLOODYGOODTIME
|
||||
return "Bloody Good Time (2010)";
|
||||
#elif SOURCE_ENGINE == SE_ALIENSWARM
|
||||
return "Alien Swarm (2010)";
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
|
||||
return "Left 4 Dead 2 (2009)";
|
||||
#elif SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
return "Nuclear Dawn (2011)";
|
||||
#elif SOURCE_ENGINE == SE_CONTAGION
|
||||
return "Contagion (2013)";
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD
|
||||
return "Left 4 Dead (2008)";
|
||||
#elif SOURCE_ENGINE == SE_ORANGEBOX
|
||||
return "Episode 2 (Orange Box, 2007)";
|
||||
#elif SOURCE_ENGINE == SE_CSS
|
||||
return "Counter-Strike: Source (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_HL2DM
|
||||
return "Half-Life 2 Deathmatch (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_DODS
|
||||
return "Day of Defeat: Source (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_SDK2013
|
||||
return "Source SDK 2013 (2013)";
|
||||
#elif SOURCE_ENGINE == SE_BMS
|
||||
return "Black Mesa (2015)";
|
||||
#elif SOURCE_ENGINE == SE_TF2
|
||||
return "Team Fortress 2 (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_DARKMESSIAH
|
||||
return "Dark Messiah (2006)";
|
||||
#elif SOURCE_ENGINE == SE_EYE
|
||||
return "E.Y.E. Divine Cybermancy (2011)";
|
||||
#elif SOURCE_ENGINE == SE_PORTAL2
|
||||
return "Portal 2 (2011)";
|
||||
#elif SOURCE_ENGINE == SE_BLADE
|
||||
return "Blade Symphony (2013)";
|
||||
#elif SOURCE_ENGINE == SE_INSURGENCY
|
||||
return "Insurgency (2013)";
|
||||
#elif SOURCE_ENGINE == SE_DOI
|
||||
return "Day of Infamy (2016)";
|
||||
#elif SOURCE_ENGINE == SE_CSGO
|
||||
return "Counter-Strike: Global Offensive (2012)";
|
||||
#elif SOURCE_ENGINE == SE_DOTA
|
||||
return "Dota 2 (2013)";
|
||||
#elif SOURCE_ENGINE == SE_EPISODEONE
|
||||
if (g_bOriginalEngine)
|
||||
{
|
||||
return "Original (pre-Episode 1)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Episode 1 (2004)";
|
||||
}
|
||||
#elif SOURCE_ENGINE == SE_MOCK
|
||||
return "Mock";
|
||||
#elif SOURCE_ENGINE == SE_PVKII
|
||||
return "Pirates, Vikings, and Knights II";
|
||||
#elif SOURCE_ENGINE == SE_MCV
|
||||
return "Military Combat: Vietnam";
|
||||
#else
|
||||
#error "SOURCE_ENGINE not defined to a known value"
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
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;
|
||||
};
|
||||
#else
|
||||
class GlobCommand : public IMetamodSourceCommandInfo
|
||||
{
|
||||
public:
|
||||
unsigned int GetArgCount()
|
||||
{
|
||||
return engine->Cmd_Argc() - 1;
|
||||
}
|
||||
|
||||
const char *GetArg(unsigned int num)
|
||||
{
|
||||
return engine->Cmd_Argv(num);
|
||||
}
|
||||
|
||||
const char *GetArgString()
|
||||
{
|
||||
return engine->Cmd_Args();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void LocalCommand_Meta(const CCommand &args)
|
||||
{
|
||||
GlobCommand cmd(&args);
|
||||
#else
|
||||
void LocalCommand_Meta()
|
||||
{
|
||||
GlobCommand cmd;
|
||||
#endif
|
||||
Command_Meta(&cmd);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_DOTA
|
||||
void ClientCommand(CEntityIndex index, const CCommand &_cmd)
|
||||
{
|
||||
int client = index.Get();
|
||||
GlobCommand cmd(&_cmd);
|
||||
#elif SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void ClientCommand(edict_t *client, const CCommand &_cmd)
|
||||
{
|
||||
GlobCommand cmd(&_cmd);
|
||||
#else
|
||||
void ClientCommand(edict_t *client)
|
||||
{
|
||||
GlobCommand cmd;
|
||||
#endif
|
||||
if (strcmp(cmd.GetArg(0), "meta") == 0)
|
||||
{
|
||||
Command_ClientMeta(client, &cmd);
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
|
||||
|
||||
void CacheUserMessages()
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* This only gets called if IServerGameDLL::GetUserMessageInfo() triggers it */
|
||||
void Detour_Error(const tchar *pMsg, ...)
|
||||
{
|
||||
/* Jump back to setjmp() in CacheUserMessages() */
|
||||
longjmp(usermsg_end, 1);
|
||||
}
|
||||
|
||||
#define IA32_JMP_IMM32 0xE9
|
||||
|
||||
/* IServerGameDLL::GetUserMessageInfo() crashes on games based on the old engine and
|
||||
* early Orange Box. This is because Error() from tier0 gets called when a bad index is
|
||||
* passed. This is all due to a bug in CUtlRBTree::IsValidIndex().
|
||||
*
|
||||
* So we detour Error() to fix this. Our detour then jumps back into CacheUserMessages()
|
||||
* to a point before GetUserMessageInfo() is called. The detour is then removed and we
|
||||
* exit.
|
||||
*/
|
||||
void CacheUserMessages()
|
||||
{
|
||||
int q, size;
|
||||
char buffer[256];
|
||||
unsigned char *target, *detour;
|
||||
unsigned char orig_bytes[5];
|
||||
|
||||
target = (unsigned char *)&Error;
|
||||
detour = (unsigned char *)&Detour_Error;
|
||||
|
||||
/* Save bytes from target function */
|
||||
memcpy(orig_bytes, target, sizeof(orig_bytes));
|
||||
|
||||
/* Patch in relative jump to our Error() detour */
|
||||
SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
|
||||
target[0] = IA32_JMP_IMM32;
|
||||
*(int32_t *)&target[1] = (int32_t)(detour - (target + 5));
|
||||
|
||||
/* This is where longjmp() will end up */
|
||||
if (setjmp(usermsg_end))
|
||||
{
|
||||
/* Restore bytes and memory protection */
|
||||
memcpy(target, orig_bytes, sizeof(orig_bytes));
|
||||
SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ|SH_MEM_EXEC);
|
||||
return;
|
||||
}
|
||||
|
||||
q = 0;
|
||||
|
||||
/* If GetUserMessageInfo() calls Error(), we should end up in our detour */
|
||||
while (server->GetUserMessageInfo(q, buffer, sizeof(buffer), size))
|
||||
{
|
||||
usermsgs_list.push_back(UsrMsgInfo(size, buffer));
|
||||
q++;
|
||||
}
|
||||
|
||||
/* Jump back to setjmp() */
|
||||
longjmp(usermsg_end, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
|
||||
{
|
||||
Assert(filesystem);
|
||||
#ifdef _MSC_VER
|
||||
Assert(_heapchk() == _HEAPOK);
|
||||
#endif
|
||||
|
||||
FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
// load file into a null-terminated buffer
|
||||
int fileSize = filesystem->Size(f);
|
||||
char *buffer = (char *)MemAllocScratch(fileSize + 1);
|
||||
|
||||
Assert(buffer);
|
||||
|
||||
filesystem->Read(buffer, fileSize, f); // read into local buffer
|
||||
|
||||
buffer[fileSize] = 0; // null terminate file as EOF
|
||||
|
||||
filesystem->Close( f ); // close file after reading
|
||||
|
||||
bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
|
||||
|
||||
MemFreeScratch();
|
||||
|
||||
return retOK;
|
||||
va_list ap;
|
||||
char buffer[2048];
|
||||
|
||||
va_start(ap, fmt);
|
||||
UTIL_FormatArgs(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
DevMsg("%s", buffer);
|
||||
}
|
||||
|
@ -50,41 +50,47 @@ class INetworkGameServer;
|
||||
|
||||
class BaseProvider : public IMetamodSourceProvider
|
||||
{
|
||||
public:
|
||||
public: // Must implement
|
||||
virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) override = 0;
|
||||
virtual void Notify_DLLShutdown_Pre() override = 0;
|
||||
virtual int DetermineSourceEngine() override = 0;
|
||||
virtual const char *GetEngineDescription() const override = 0;
|
||||
virtual void GetGamePath(char *pszBuffer, int len) override = 0;
|
||||
virtual const char *GetGameDescription() override = 0;
|
||||
virtual bool ProcessVDF(const char* file, char path[], size_t path_len, char alias[], size_t alias_len) override = 0;
|
||||
virtual void ConsolePrint(const char* msg) override = 0;
|
||||
virtual void ClientConsolePrint(edict_t* client, const char* msg) override = 0;
|
||||
virtual void ServerCommand(const char* cmd) override = 0;
|
||||
virtual ConVar* CreateConVar(const char* name,
|
||||
const char* defval,
|
||||
const char* help,
|
||||
int flags) override = 0;
|
||||
virtual const char* GetConVarString(ConVar* convar) override = 0;
|
||||
virtual void SetConVarString(ConVar* convar, const char* str) override = 0;
|
||||
virtual IConCommandBaseAccessor* GetConCommandBaseAccessor() override = 0;
|
||||
virtual bool RegisterConCommandBase(ConCommandBase* pCommand) override = 0;
|
||||
virtual void UnregisterConCommandBase(ConCommandBase* pCommand) override = 0;
|
||||
virtual bool IsConCommandBaseACommand(ConCommandBase* pCommand) override = 0;
|
||||
public: // May implement/override (stubbed)
|
||||
virtual int GetUserMessageCount() override { return -1; }
|
||||
virtual int FindUserMessage(const char *name, int *size=nullptr) override { return -1;}
|
||||
virtual const char *GetUserMessage(int index, int *size=nullptr) override { return nullptr;}
|
||||
public: // May implement/override
|
||||
virtual bool IsSourceEngineBuildCompatible(int build) override;
|
||||
virtual bool GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo) override;
|
||||
virtual bool LogMessage(const char *buffer) override;
|
||||
virtual const char *GetCommandLineValue(const char *key, const char *defval) override;
|
||||
virtual void ConsolePrint(const char *msg) override;
|
||||
virtual bool IsRemotePrintingAvailable() override;
|
||||
virtual void ClientConsolePrint(edict_t *client, const char *msg) override;
|
||||
virtual void DisplayError(const char *fmt, ...) override;
|
||||
virtual void DisplayWarning(const char *fmt, ...) override;
|
||||
virtual void DisplayDevMsg(const char* fmt, ...) override;
|
||||
virtual int TryServerGameDLL(const char *iface) override;
|
||||
virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) override;
|
||||
void Notify_DLLShutdown_Pre() override;
|
||||
virtual void ServerCommand(const char *cmd) override;
|
||||
virtual ConVar *CreateConVar(const char *name,
|
||||
const char *defval,
|
||||
const char *help,
|
||||
int flags) override;
|
||||
virtual const char *GetConVarString(ConVar *convar) override;
|
||||
virtual void SetConVarString(ConVar *convar, const char *str) override;
|
||||
virtual void GetGamePath(char *pszBuffer, int len) override;
|
||||
virtual const char *GetGameDescription() override;
|
||||
virtual IConCommandBaseAccessor *GetConCommandBaseAccessor() override;
|
||||
virtual bool RegisterConCommandBase(ConCommandBase *pCommand) override;
|
||||
virtual void UnregisterConCommandBase(ConCommandBase *pCommand) override;
|
||||
virtual bool IsConCommandBaseACommand(ConCommandBase *pCommand) override;
|
||||
virtual int GetUserMessageCount() override;
|
||||
virtual int FindUserMessage(const char *name, int *size=NULL) override;
|
||||
virtual const char *GetUserMessage(int index, int *size=NULL) override;
|
||||
virtual int DetermineSourceEngine() override;
|
||||
virtual bool ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len) override;
|
||||
virtual const char *GetEngineDescription() const override;
|
||||
#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 )
|
||||
bool AllowDedicatedServers(EUniverse universe) const;
|
||||
#endif
|
||||
public:
|
||||
void SetCallbacks(IMetamodSourceProviderCallbacks* pCallbacks) override final
|
||||
{
|
||||
m_pCallbacks = pCallbacks;
|
||||
}
|
||||
protected:
|
||||
IMetamodSourceProviderCallbacks* m_pCallbacks = nullptr;
|
||||
};
|
||||
|
||||
extern IVEngineServer *engine;
|
||||
|
@ -24,6 +24,625 @@
|
||||
*/
|
||||
|
||||
#include "provider_source.h"
|
||||
#include "../console.h"
|
||||
#include <metamod.h>
|
||||
#include <metamod_util.h>
|
||||
#include <metamod_console.h>
|
||||
#include <KeyValues.h>
|
||||
#include <filesystem.h>
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void LocalCommand_Meta(const CCommand& args);
|
||||
#else
|
||||
void LocalCommand_Meta();
|
||||
#endif
|
||||
|
||||
ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options");
|
||||
|
||||
SH_DECL_HOOK0(IServerGameDLL, GameInit, SH_NOATTRIB, 0, bool);
|
||||
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, const char*, const char*, const char*, const char*, bool, bool);
|
||||
SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, 0);
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t*, const CCommand&);
|
||||
#else
|
||||
SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t*);
|
||||
#endif
|
||||
|
||||
void SourceProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
|
||||
CreateInterfaceFn serverFactory)
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_SDK2013
|
||||
// Shim to avoid hooking shims
|
||||
engine = (IVEngineServer*)((engineFactory)("VEngineServer023", NULL));
|
||||
if (!engine)
|
||||
{
|
||||
engine = (IVEngineServer*)((engineFactory)("VEngineServer022", NULL));
|
||||
if (!engine)
|
||||
{
|
||||
engine = (IVEngineServer*)((engineFactory)("VEngineServer021", NULL));
|
||||
}
|
||||
}
|
||||
#else
|
||||
engine = (IVEngineServer*)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
|
||||
#endif
|
||||
if (!engine)
|
||||
{
|
||||
DisplayError("Could not find IVEngineServer! Metamod cannot load.");
|
||||
return;
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
icvar = (ICvar*)((engineFactory)(CVAR_INTERFACE_VERSION, NULL));
|
||||
#else
|
||||
icvar = (ICvar*)((engineFactory)(VENGINE_CVAR_INTERFACE_VERSION, NULL));
|
||||
#endif
|
||||
if (!icvar)
|
||||
{
|
||||
DisplayError("Could not find ICvar! Metamod cannot load.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gameclients = (IServerGameClients*)(serverFactory("ServerGameClients003", NULL)))
|
||||
== NULL)
|
||||
{
|
||||
gameclients = (IServerGameClients*)(serverFactory("ServerGameClients004", NULL));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
g_pCVar = icvar;
|
||||
#endif
|
||||
|
||||
g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd);
|
||||
|
||||
#if SOURCE_ENGINE == SE_EPISODEONE
|
||||
/* The Ship is the only game known at this time that uses the pre-Episode One engine */
|
||||
bOriginalEngine = strcmp(CommandLine()->ParmValue("-game", "hl2"), "ship") == 0;
|
||||
#endif
|
||||
|
||||
CacheUserMessages();
|
||||
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
if (!g_SMConVarAccessor.InitConCommandBaseList())
|
||||
{
|
||||
/* This is very unlikely considering it's old engine */
|
||||
mm_LogMessage("[META] Warning: Failed to find ConCommandBase list!");
|
||||
mm_LogMessage("[META] Warning: ConVars and ConCommands cannot be unregistered properly! Please file a bug report.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gameclients)
|
||||
{
|
||||
SH_ADD_HOOK(IServerGameClients, ClientCommand, gameclients, SH_MEMBER(this, &SourceProvider::Hook_ClientCommand), false);
|
||||
}
|
||||
|
||||
SH_ADD_HOOK(IServerGameDLL, GameInit, server, SH_MEMBER(this, &SourceProvider::Hook_GameInit), false);
|
||||
SH_ADD_HOOK(IServerGameDLL, LevelInit, server, SH_MEMBER(this, &SourceProvider::Hook_LevelInit), true);
|
||||
SH_ADD_HOOK(IServerGameDLL, LevelShutdown, server, SH_MEMBER(this, &SourceProvider::Hook_LevelShutdown), true);
|
||||
}
|
||||
|
||||
void SourceProvider::Notify_DLLShutdown_Pre()
|
||||
{
|
||||
SH_REMOVE_HOOK(IServerGameDLL, GameInit, server, SH_MEMBER(this, &SourceProvider::Hook_GameInit), false);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, LevelInit, server, SH_MEMBER(this, &SourceProvider::Hook_LevelInit), true);
|
||||
SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, server, SH_MEMBER(this, &SourceProvider::Hook_LevelShutdown), true);
|
||||
|
||||
g_SMConVarAccessor.RemoveMetamodCommands();
|
||||
|
||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||
if (g_Metamod.IsLoadedAsGameDLL())
|
||||
{
|
||||
icvar->UnlinkVariables(FCVAR_GAMEDLL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SourceProvider::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");
|
||||
|
||||
if (bOriginalEngine)
|
||||
{
|
||||
/* The Ship must use a special version of this function */
|
||||
bKVLoaded = KVLoadFromFile(pValues, baseFs, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
bKVLoaded = pValues->LoadFromFile(baseFs, file);
|
||||
}
|
||||
|
||||
if (!bKVLoaded)
|
||||
{
|
||||
pValues->deleteThis();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((plugin_file = pValues->GetString("file", NULL)) == NULL)
|
||||
{
|
||||
pValues->deleteThis();
|
||||
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, "");
|
||||
}
|
||||
|
||||
pValues->deleteThis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SourceProvider::DetermineSourceEngine()
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_BLOODYGOODTIME
|
||||
return SOURCE_ENGINE_BLOODYGOODTIME;
|
||||
#elif SOURCE_ENGINE == SE_ALIENSWARM
|
||||
return SOURCE_ENGINE_ALIENSWARM;
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
|
||||
return SOURCE_ENGINE_LEFT4DEAD2;
|
||||
#elif SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
return SOURCE_ENGINE_NUCLEARDAWN;
|
||||
#elif SOURCE_ENGINE == SE_CONTAGION
|
||||
return SOURCE_ENGINE_CONTAGION;
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD
|
||||
return SOURCE_ENGINE_LEFT4DEAD;
|
||||
#elif SOURCE_ENGINE == SE_ORANGEBOX
|
||||
return SOURCE_ENGINE_ORANGEBOX;
|
||||
#elif SOURCE_ENGINE == SE_CSS
|
||||
return SOURCE_ENGINE_CSS;
|
||||
#elif SOURCE_ENGINE == SE_HL2DM
|
||||
return SOURCE_ENGINE_HL2DM;
|
||||
#elif SOURCE_ENGINE == SE_DODS
|
||||
return SOURCE_ENGINE_DODS;
|
||||
#elif SOURCE_ENGINE == SE_SDK2013
|
||||
return SOURCE_ENGINE_SDK2013;
|
||||
#elif SOURCE_ENGINE == SE_TF2
|
||||
return SOURCE_ENGINE_TF2;
|
||||
#elif SOURCE_ENGINE == SE_DARKMESSIAH
|
||||
return SOURCE_ENGINE_DARKMESSIAH;
|
||||
#elif SOURCE_ENGINE == SE_EYE
|
||||
return SOURCE_ENGINE_EYE;
|
||||
#elif SOURCE_ENGINE == SE_PORTAL2
|
||||
return SOURCE_ENGINE_PORTAL2;
|
||||
#elif SOURCE_ENGINE == SE_BLADE
|
||||
return SOURCE_ENGINE_BLADE;
|
||||
#elif SOURCE_ENGINE == SE_INSURGENCY
|
||||
return SOURCE_ENGINE_INSURGENCY;
|
||||
#elif SOURCE_ENGINE == SE_DOI
|
||||
return SOURCE_ENGINE_DOI;
|
||||
#elif SOURCE_ENGINE == SE_CSGO
|
||||
return SOURCE_ENGINE_CSGO;
|
||||
#elif SOURCE_ENGINE == SE_BMS
|
||||
return SOURCE_ENGINE_BMS;
|
||||
#elif SOURCE_ENGINE == SE_EPISODEONE
|
||||
return g_bOriginalEngine ? SOURCE_ENGINE_ORIGINAL : SOURCE_ENGINE_EPISODEONE;
|
||||
#elif SOURCE_ENGINE == SE_MOCK
|
||||
return SOURCE_ENGINE_MOCK;
|
||||
#elif SOURCE_ENGINE == SE_PVKII
|
||||
return SOURCE_ENGINE_PVKII;
|
||||
#elif SOURCE_ENGINE == SE_MCV
|
||||
return SOURCE_ENGINE_MCV;
|
||||
#else
|
||||
#error "SOURCE_ENGINE not defined to a known value"
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* SourceProvider::GetEngineDescription() const
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_BLOODYGOODTIME
|
||||
return "Bloody Good Time (2010)";
|
||||
#elif SOURCE_ENGINE == SE_ALIENSWARM
|
||||
return "Alien Swarm (2010)";
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
|
||||
return "Left 4 Dead 2 (2009)";
|
||||
#elif SOURCE_ENGINE == SE_NUCLEARDAWN
|
||||
return "Nuclear Dawn (2011)";
|
||||
#elif SOURCE_ENGINE == SE_CONTAGION
|
||||
return "Contagion (2013)";
|
||||
#elif SOURCE_ENGINE == SE_LEFT4DEAD
|
||||
return "Left 4 Dead (2008)";
|
||||
#elif SOURCE_ENGINE == SE_ORANGEBOX
|
||||
return "Episode 2 (Orange Box, 2007)";
|
||||
#elif SOURCE_ENGINE == SE_CSS
|
||||
return "Counter-Strike: Source (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_HL2DM
|
||||
return "Half-Life 2 Deathmatch (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_DODS
|
||||
return "Day of Defeat: Source (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_SDK2013
|
||||
return "Source SDK 2013 (2013)";
|
||||
#elif SOURCE_ENGINE == SE_BMS
|
||||
return "Black Mesa (2015)";
|
||||
#elif SOURCE_ENGINE == SE_TF2
|
||||
return "Team Fortress 2 (Valve Orange Box)";
|
||||
#elif SOURCE_ENGINE == SE_DARKMESSIAH
|
||||
return "Dark Messiah (2006)";
|
||||
#elif SOURCE_ENGINE == SE_EYE
|
||||
return "E.Y.E. Divine Cybermancy (2011)";
|
||||
#elif SOURCE_ENGINE == SE_PORTAL2
|
||||
return "Portal 2 (2011)";
|
||||
#elif SOURCE_ENGINE == SE_BLADE
|
||||
return "Blade Symphony (2013)";
|
||||
#elif SOURCE_ENGINE == SE_INSURGENCY
|
||||
return "Insurgency (2013)";
|
||||
#elif SOURCE_ENGINE == SE_DOI
|
||||
return "Day of Infamy (2016)";
|
||||
#elif SOURCE_ENGINE == SE_CSGO
|
||||
return "Counter-Strike: Global Offensive (2012)";
|
||||
#elif SOURCE_ENGINE == SE_EPISODEONE
|
||||
if (g_bOriginalEngine)
|
||||
{
|
||||
return "Original (pre-Episode 1)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Episode 1 (2004)";
|
||||
}
|
||||
#elif SOURCE_ENGINE == SE_MOCK
|
||||
return "Mock";
|
||||
#elif SOURCE_ENGINE == SE_PVKII
|
||||
return "Pirates, Vikings, and Knights II";
|
||||
#elif SOURCE_ENGINE == SE_MCV
|
||||
return "Military Combat: Vietnam";
|
||||
#else
|
||||
#error "SOURCE_ENGINE not defined to a known value"
|
||||
#endif
|
||||
}
|
||||
|
||||
void SourceProvider::GetGamePath(char* pszBuffer, int len)
|
||||
{
|
||||
engine->GetGameDir(pszBuffer, len);
|
||||
}
|
||||
|
||||
const char* SourceProvider::GetGameDescription()
|
||||
{
|
||||
return server->GetGameDescription();
|
||||
}
|
||||
|
||||
void SourceProvider::ConsolePrint(const char* str)
|
||||
{
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
ConMsg("%s", str);
|
||||
#else
|
||||
Msg("%s", str);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SourceProvider::ClientConsolePrint(edict_t* pEdict, const char* message)
|
||||
{
|
||||
engine->ClientPrintf(pEdict, message);
|
||||
}
|
||||
|
||||
void SourceProvider::ServerCommand(const char* cmd)
|
||||
{
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
|
||||
const char* SourceProvider::GetConVarString(ConVar* convar)
|
||||
{
|
||||
if (convar == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return convar->GetString();
|
||||
}
|
||||
|
||||
void SourceProvider::SetConVarString(ConVar* convar, const char* str)
|
||||
{
|
||||
convar->SetValue(str);
|
||||
}
|
||||
|
||||
bool SourceProvider::IsConCommandBaseACommand(ConCommandBase* pCommand)
|
||||
{
|
||||
return pCommand->IsCommand();
|
||||
}
|
||||
|
||||
IConCommandBaseAccessor* SourceProvider::GetConCommandBaseAccessor()
|
||||
{
|
||||
return &g_SMConVarAccessor;
|
||||
}
|
||||
|
||||
bool SourceProvider::RegisterConCommandBase(ConCommandBase* pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Register(pCommand);
|
||||
}
|
||||
|
||||
void SourceProvider::UnregisterConCommandBase(ConCommandBase* pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Unregister(pCommand);
|
||||
}
|
||||
|
||||
ConVar* SourceProvider::CreateConVar(const char* name,
|
||||
const char* defval,
|
||||
const char* help,
|
||||
int flags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
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;
|
||||
};
|
||||
#else
|
||||
class GlobCommand : public IMetamodSourceCommandInfo
|
||||
{
|
||||
public:
|
||||
unsigned int GetArgCount()
|
||||
{
|
||||
return engine->Cmd_Argc() - 1;
|
||||
}
|
||||
|
||||
const char* GetArg(unsigned int num)
|
||||
{
|
||||
return engine->Cmd_Argv(num);
|
||||
}
|
||||
|
||||
const char* GetArgString()
|
||||
{
|
||||
return engine->Cmd_Args();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void LocalCommand_Meta(const CCommand& args)
|
||||
{
|
||||
GlobCommand cmd(&args);
|
||||
#else
|
||||
void LocalCommand_Meta()
|
||||
{
|
||||
GlobCommand cmd;
|
||||
#endif
|
||||
Command_Meta(&cmd);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
|
||||
|
||||
void SourceProvider::CacheUserMessages()
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
static jmp_buf usermsg_end;
|
||||
|
||||
/* This only gets called if IServerGameDLL::GetUserMessageInfo() triggers it */
|
||||
void SourceProvider::Detour_Error(const tchar* pMsg, ...)
|
||||
{
|
||||
/* Jump back to setjmp() in CacheUserMessages() */
|
||||
longjmp(usermsg_end, 1);
|
||||
}
|
||||
|
||||
#define IA32_JMP_IMM32 0xE9
|
||||
|
||||
/* IServerGameDLL::GetUserMessageInfo() crashes on games based on the old engine and
|
||||
* early Orange Box. This is because Error() from tier0 gets called when a bad index is
|
||||
* passed. This is all due to a bug in CUtlRBTree::IsValidIndex().
|
||||
*
|
||||
* So we detour Error() to fix this. Our detour then jumps back into CacheUserMessages()
|
||||
* to a point before GetUserMessageInfo() is called. The detour is then removed and we
|
||||
* exit.
|
||||
*/
|
||||
void SourceProvider::CacheUserMessages()
|
||||
{
|
||||
int q, size;
|
||||
char buffer[256];
|
||||
unsigned char* target, * detour;
|
||||
unsigned char orig_bytes[5];
|
||||
|
||||
target = (unsigned char*)&Error;
|
||||
detour = (unsigned char*)&Detour_Error;
|
||||
|
||||
/* Save bytes from target function */
|
||||
memcpy(orig_bytes, target, sizeof(orig_bytes));
|
||||
|
||||
/* Patch in relative jump to our Error() detour */
|
||||
SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
|
||||
target[0] = IA32_JMP_IMM32;
|
||||
*(int32_t*)&target[1] = (int32_t)(detour - (target + 5));
|
||||
|
||||
/* This is where longjmp() will end up */
|
||||
if (setjmp(usermsg_end))
|
||||
{
|
||||
/* Restore bytes and memory protection */
|
||||
memcpy(target, orig_bytes, sizeof(orig_bytes));
|
||||
SetMemAccess(target, sizeof(orig_bytes), SH_MEM_READ | SH_MEM_EXEC);
|
||||
return;
|
||||
}
|
||||
|
||||
q = 0;
|
||||
|
||||
/* If GetUserMessageInfo() calls Error(), we should end up in our detour */
|
||||
while (server->GetUserMessageInfo(q, buffer, sizeof(buffer), size))
|
||||
{
|
||||
usermsgs_list.push_back(UsrMsgInfo(size, buffer));
|
||||
q++;
|
||||
}
|
||||
|
||||
/* Jump back to setjmp() */
|
||||
longjmp(usermsg_end, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int SourceProvider::GetUserMessageCount()
|
||||
{
|
||||
if (!IsUserMessageIterationSupported())
|
||||
return -1;
|
||||
|
||||
return (int)usermsgs_list.size();
|
||||
}
|
||||
|
||||
int SourceProvider::FindUserMessage(const char* name, int* size)
|
||||
{
|
||||
if (IsUserMessageIterationSupported())
|
||||
{
|
||||
for (size_t i = 0; i < usermsgs_list.size(); i++)
|
||||
{
|
||||
if (usermsgs_list[i].name.compare(name) == 0)
|
||||
{
|
||||
if (size)
|
||||
{
|
||||
*size = usermsgs_list[i].size;
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* SourceProvider::GetUserMessage(int index, int* size)
|
||||
{
|
||||
if (!IsUserMessageIterationSupported() || index < 0 || index >= (int)usermsgs_list.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (size)
|
||||
{
|
||||
*size = usermsgs_list[index].size;
|
||||
}
|
||||
|
||||
return usermsgs_list[index].name.c_str();
|
||||
}
|
||||
|
||||
bool SourceProvider::KVLoadFromFile(KeyValues* kv, IFileSystem* filesystem, const char* resourceName, const char* pathID)
|
||||
{
|
||||
Assert(filesystem);
|
||||
#ifdef _MSC_VER
|
||||
Assert(_heapchk() == _HEAPOK);
|
||||
#endif
|
||||
|
||||
FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
// load file into a null-terminated buffer
|
||||
int fileSize = filesystem->Size(f);
|
||||
char* buffer = (char*)MemAllocScratch(fileSize + 1);
|
||||
|
||||
Assert(buffer);
|
||||
|
||||
filesystem->Read(buffer, fileSize, f); // read into local buffer
|
||||
|
||||
buffer[fileSize] = 0; // null terminate file as EOF
|
||||
|
||||
filesystem->Close(f); // close file after reading
|
||||
|
||||
bool retOK = kv->LoadFromBuffer(resourceName, buffer, filesystem);
|
||||
|
||||
MemFreeScratch();
|
||||
|
||||
return retOK;
|
||||
}
|
||||
|
||||
bool SourceProvider::Hook_GameInit()
|
||||
{
|
||||
if (nullptr != m_pCallbacks)
|
||||
{
|
||||
m_pCallbacks->OnGameInit();
|
||||
}
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
bool SourceProvider::Hook_LevelInit(char const* pMapName, char const* pMapEntities, char const* pOldLevel,
|
||||
char const* pLandmarkName, bool loadGame, bool background)
|
||||
{
|
||||
if (nullptr != m_pCallbacks)
|
||||
{
|
||||
m_pCallbacks->OnLevelInit(pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background);
|
||||
}
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
void SourceProvider::Hook_LevelShutdown()
|
||||
{
|
||||
if (nullptr != m_pCallbacks)
|
||||
{
|
||||
m_pCallbacks->OnLevelShutdown();
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void SourceProvider::Hook_ClientCommand(edict_t* client, const CCommand& _cmd)
|
||||
{
|
||||
GlobCommand cmd(&_cmd);
|
||||
#else
|
||||
void SourceProvider::Hook_ClientCommand(edict_t * client)
|
||||
{
|
||||
GlobCommand cmd;
|
||||
#endif
|
||||
if (strcmp(cmd.GetArg(0), "meta") == 0)
|
||||
{
|
||||
Command_ClientMeta(client, &cmd);
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static SourceProvider g_SourceProvider;
|
||||
|
||||
|
@ -29,10 +29,73 @@
|
||||
#define _INCLUDE_METAMOD_SOURCE_SOURCE1_PROVIDER_H_
|
||||
|
||||
#include "../provider_base.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SourceProvider : public BaseProvider
|
||||
{
|
||||
|
||||
public:
|
||||
virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) override;
|
||||
virtual void Notify_DLLShutdown_Pre() override;
|
||||
virtual bool ProcessVDF(const char* file, char path[], size_t path_len, char alias[], size_t alias_len) override;
|
||||
virtual int DetermineSourceEngine() override;
|
||||
virtual const char* GetEngineDescription() const override;
|
||||
virtual void GetGamePath(char* pszBuffer, int len) override;
|
||||
virtual const char* GetGameDescription() override;
|
||||
virtual void ConsolePrint(const char* msg) override;
|
||||
virtual void ClientConsolePrint(edict_t* client, const char* msg) override;
|
||||
virtual void ServerCommand(const char* cmd) override;
|
||||
virtual ConVar* CreateConVar(const char* name,
|
||||
const char* defval,
|
||||
const char* help,
|
||||
int flags) override;
|
||||
virtual const char* GetConVarString(ConVar* convar) override;
|
||||
virtual void SetConVarString(ConVar* convar, const char* str) override;
|
||||
virtual IConCommandBaseAccessor* GetConCommandBaseAccessor() override;
|
||||
virtual bool RegisterConCommandBase(ConCommandBase* pCommand) override;
|
||||
virtual void UnregisterConCommandBase(ConCommandBase* pCommand) override;
|
||||
virtual bool IsConCommandBaseACommand(ConCommandBase* pCommand) override;
|
||||
virtual int GetUserMessageCount() override;
|
||||
virtual int FindUserMessage(const char* name, int* size = nullptr) override;
|
||||
virtual const char* GetUserMessage(int index, int* size = nullptr) override;
|
||||
public: // Hook callbacks that map to provider callbacks
|
||||
bool Hook_GameInit();
|
||||
bool Hook_LevelInit(char const* pMapName, char const* pMapEntities, char const* pOldLevel,
|
||||
char const* pLandmarkName, bool loadGame, bool background);
|
||||
void Hook_LevelShutdown();
|
||||
#if SOURCE_ENGINE >= SE_ORANGEBOX
|
||||
void Hook_ClientCommand(edict_t* pEdict, const CCommand& args);
|
||||
#else
|
||||
void Hook_ClientCommand(edict_t* pEdict);
|
||||
#endif
|
||||
private:
|
||||
struct UsrMsgInfo
|
||||
{
|
||||
UsrMsgInfo()
|
||||
{
|
||||
}
|
||||
UsrMsgInfo(int s, const char* t) : size(s), name(t)
|
||||
{
|
||||
}
|
||||
int size;
|
||||
std::string name;
|
||||
};
|
||||
private:
|
||||
void CacheUserMessages();
|
||||
void Detour_Error(const tchar* pMsg, ...);
|
||||
bool KVLoadFromFile(KeyValues* kv, IFileSystem* filesystem, const char* resourceName, const char* pathID = nullptr);
|
||||
inline bool IsUserMessageIterationSupported() const
|
||||
{
|
||||
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
IFileSystem* baseFs = nullptr;
|
||||
std::vector<UsrMsgInfo> usermsgs_list;
|
||||
bool bOriginalEngine = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,401 @@
|
||||
*/
|
||||
|
||||
#include "provider_source2.h"
|
||||
#include "../console.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>
|
||||
|
||||
void LocalCommand_Meta(const CCommand& args);
|
||||
|
||||
ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options");
|
||||
|
||||
static ISource2ServerConfig* serverconfig = NULL;
|
||||
INetworkServerService* netservice = NULL;
|
||||
IEngineServiceMgr* enginesvcmgr = NULL;
|
||||
|
||||
// Hack to make hook decl compile when only having forward decl in header.
|
||||
// (we have class structure but it requires protobuf which we don't want to include here)
|
||||
class GameSessionConfiguration_t { };
|
||||
|
||||
SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *);
|
||||
SH_DECL_HOOK5_void(IEngineServiceMgr, SwitchToLoop, SH_NOATTRIB, 0, const char *, KeyValues *, uint32, const char *, bool);
|
||||
SH_DECL_HOOK2_void(INetworkGameServer, Init, SH_NOATTRIB, 0, const GameSessionConfiguration_t &, const char *);
|
||||
SH_DECL_HOOK3(INetworkGameServer, StartChangeLevel, SH_NOATTRIB, 0, CUtlVector<INetworkGameClient *> *, const char *, const char *, void *);
|
||||
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CEntityIndex, const CCommand&);
|
||||
|
||||
#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));
|
||||
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);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// 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.
|
||||
|
||||
char searchPath[260];
|
||||
baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath));
|
||||
for (size_t i = 0; i < sizeof(searchPath); ++i)
|
||||
{
|
||||
if (searchPath[i] == ';')
|
||||
{
|
||||
searchPath[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
baseFs->RemoveSearchPath(searchPath, "GAME");
|
||||
|
||||
// TODO: figure out why these calls get ignored and path remains
|
||||
//baseFs->RemoveSearchPath(searchPath, "CONTENT");
|
||||
//baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE");
|
||||
//baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE_MOD");
|
||||
|
||||
baseFs->RemoveSearchPaths("DEFAULT_WRITE_PATH");
|
||||
baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath));
|
||||
for (size_t i = 0; i < sizeof(searchPath); ++i)
|
||||
{
|
||||
if (searchPath[i] == ';')
|
||||
{
|
||||
searchPath[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
baseFs->AddSearchPath(searchPath, "DEFAULT_WRITE_PATH");
|
||||
#endif
|
||||
|
||||
g_pCVar = icvar;
|
||||
|
||||
g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd);
|
||||
|
||||
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(INetworkServerService, StartupServer, netservice, SH_MEMBER(this, &Source2Provider::Hook_StartupServer_Post), true);
|
||||
SH_ADD_HOOK(IEngineServiceMgr, SwitchToLoop, enginesvcmgr, SH_MEMBER(this, &Source2Provider::Hook_SwitchToLoop), false);
|
||||
}
|
||||
|
||||
void Source2Provider::Notify_DLLShutdown_Pre()
|
||||
{
|
||||
g_SMConVarAccessor.RemoveMetamodCommands();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ke::SafeSprintf(pszBuffer, len, "%s", Plat_GetGameDirectory());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void Source2Provider::ConsolePrint(const char* str)
|
||||
{
|
||||
ConMsg("%s", str);
|
||||
}
|
||||
|
||||
void Source2Provider::ClientConsolePrint(edict_t* pEdict, const char* message)
|
||||
{
|
||||
int client = (int)(pEdict - gpGlobals->pEdicts);
|
||||
engine->ClientPrintf(client, message);
|
||||
}
|
||||
|
||||
void Source2Provider::ServerCommand(const char* cmd)
|
||||
{
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
|
||||
const char* Source2Provider::GetConVarString(ConVar* convar)
|
||||
{
|
||||
if (convar == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return convar->GetString();
|
||||
}
|
||||
|
||||
void Source2Provider::SetConVarString(ConVar* convar, const char* str)
|
||||
{
|
||||
convar->SetValue(str);
|
||||
}
|
||||
|
||||
bool Source2Provider::IsConCommandBaseACommand(ConCommandBase* pCommand)
|
||||
{
|
||||
return pCommand->IsCommand();
|
||||
}
|
||||
|
||||
IConCommandBaseAccessor* Source2Provider::GetConCommandBaseAccessor()
|
||||
{
|
||||
return &g_SMConVarAccessor;
|
||||
}
|
||||
|
||||
bool Source2Provider::RegisterConCommandBase(ConCommandBase* pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Register(pCommand);
|
||||
}
|
||||
|
||||
void Source2Provider::UnregisterConCommandBase(ConCommandBase* pCommand)
|
||||
{
|
||||
return g_SMConVarAccessor.Unregister(pCommand);
|
||||
}
|
||||
|
||||
ConVar* Source2Provider::CreateConVar(const char* name,
|
||||
const char* defval,
|
||||
const char* help,
|
||||
int flags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 CCommand& args)
|
||||
{
|
||||
GlobCommand cmd(&args);
|
||||
Command_Meta(&cmd);
|
||||
}
|
||||
|
||||
bool Source2Provider::KVLoadFromFile(KeyValues* kv, IFileSystem* filesystem, const char* resourceName, const char* pathID)
|
||||
{
|
||||
Assert(filesystem);
|
||||
#ifdef _MSC_VER
|
||||
Assert(_heapchk() == _HEAPOK);
|
||||
#endif
|
||||
|
||||
FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
// load file into a null-terminated buffer
|
||||
int fileSize = filesystem->Size(f);
|
||||
char* buffer = (char*)MemAllocScratch(fileSize + 1);
|
||||
|
||||
Assert(buffer);
|
||||
|
||||
filesystem->Read(buffer, fileSize, f); // read into local buffer
|
||||
|
||||
buffer[fileSize] = 0; // null terminate file as EOF
|
||||
|
||||
filesystem->Close(f); // close file after reading
|
||||
|
||||
bool retOK = kv->LoadFromBuffer(resourceName, buffer, filesystem);
|
||||
|
||||
MemFreeScratch();
|
||||
|
||||
return retOK;
|
||||
}
|
||||
|
||||
void Source2Provider::Hook_StartupServer_Post(const GameSessionConfiguration_t &config, ISource2WorldSession *, const char *)
|
||||
{
|
||||
static bool bGameServerHooked = false;
|
||||
if (!bGameServerHooked)
|
||||
{
|
||||
INetworkGameServer* netserver = (META_IFACEPTR(INetworkServerService))->GetIGameServer();
|
||||
|
||||
SH_ADD_VPHOOK(INetworkGameServer, Init, netserver, SH_MEMBER(this, &Source2Provider::Hook_Init), false);
|
||||
SH_ADD_VPHOOK(INetworkGameServer, StartChangeLevel, netserver, SH_MEMBER(this, &Source2Provider::Hook_StartChangeLevel), false);
|
||||
|
||||
bGameServerHooked = true;
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
void Source2Provider::Hook_Init(const GameSessionConfiguration_t &config, const char *pszMapName)
|
||||
{
|
||||
static char szLastMap[260] = "";
|
||||
if (nullptr != m_pCallbacks)
|
||||
{
|
||||
m_pCallbacks->OnLevelInit(pszMapName, "", sLastMap.c_str(), "", false, false);
|
||||
}
|
||||
|
||||
sLastMap = pszMapName;
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
CUtlVector<INetworkGameClient *> *Source2Provider::Hook_StartChangeLevel(const char *, const char *, void *)
|
||||
{
|
||||
if (nullptr != m_pCallbacks)
|
||||
{
|
||||
m_pCallbacks->OnLevelShutdown();
|
||||
}
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, nullptr);
|
||||
}
|
||||
|
||||
void Source2Provider::Hook_SwitchToLoop(const char *pszLoopName, KeyValues *pKV, uint32 nId, const char *pszUnk, bool bUnk)
|
||||
{
|
||||
if (nullptr != m_pCallbacks && strcmp(pszLoopName, "levelload") == 0)
|
||||
{
|
||||
m_pCallbacks->OnGameInit();
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
void Source2Provider::Hook_ClientCommand(CEntityIndex index, const CCommand& _cmd)
|
||||
{
|
||||
int client = index.Get();
|
||||
GlobCommand cmd(&_cmd);
|
||||
|
||||
if (strcmp(cmd.GetArg(0), "meta") == 0)
|
||||
{
|
||||
Command_ClientMeta(client, &cmd);
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
static Source2Provider g_Source2Provider;
|
||||
|
||||
|
@ -29,10 +29,54 @@
|
||||
#define _INCLUDE_METAMOD_SOURCE_SOURCE2_PROVIDER_H_
|
||||
|
||||
#include "../provider_base.h"
|
||||
#include <tier1/utlvector.h>
|
||||
#include <string>
|
||||
|
||||
// TODO: is this still needed for Dota or CS2 on any platform?
|
||||
#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 )
|
||||
#define SHOULD_OVERRIDE_ALLOWDEDICATED_SERVER
|
||||
#endif
|
||||
|
||||
class INetworkGameClient;
|
||||
class ISource2WorldSession;
|
||||
|
||||
class Source2Provider : public BaseProvider
|
||||
{
|
||||
|
||||
public:
|
||||
virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) override;
|
||||
virtual void Notify_DLLShutdown_Pre() override;
|
||||
virtual bool ProcessVDF(const char* file, char path[], size_t path_len, char alias[], size_t alias_len) override;
|
||||
virtual int DetermineSourceEngine() override;
|
||||
virtual const char* GetEngineDescription() const override;
|
||||
virtual void GetGamePath(char* pszBuffer, int len) override;
|
||||
virtual const char* GetGameDescription() override;
|
||||
virtual void ConsolePrint(const char* msg) override;
|
||||
virtual void ClientConsolePrint(edict_t* client, const char* msg) override;
|
||||
virtual void ServerCommand(const char* cmd) override;
|
||||
virtual ConVar* CreateConVar(const char* name,
|
||||
const char* defval,
|
||||
const char* help,
|
||||
int flags) override;
|
||||
virtual const char* GetConVarString(ConVar* convar) override;
|
||||
virtual void SetConVarString(ConVar* convar, const char* str) override;
|
||||
virtual IConCommandBaseAccessor* GetConCommandBaseAccessor() override;
|
||||
virtual bool RegisterConCommandBase(ConCommandBase* pCommand) override;
|
||||
virtual void UnregisterConCommandBase(ConCommandBase* pCommand) override;
|
||||
virtual bool IsConCommandBaseACommand(ConCommandBase* pCommand) override;
|
||||
public:
|
||||
#ifdef SHOULD_OVERRIDE_ALLOWDEDICATED_SERVER
|
||||
bool Hook_AllowDedicatedServers(EUniverse universe) const;
|
||||
#endif
|
||||
void Hook_StartupServer_Post(const GameSessionConfiguration_t &config, ISource2WorldSession *, const char *);
|
||||
void Hook_Init(const GameSessionConfiguration_t &config, const char* pszMapName);
|
||||
CUtlVector<INetworkGameClient *> *Hook_StartChangeLevel(const char*, const char*, void*);
|
||||
void Hook_SwitchToLoop(const char *pszLoopName, KeyValues *pKV, uint32 nId, const char *pszUnk, bool bUnk);
|
||||
void Hook_ClientCommand(CEntityIndex index, const CCommand& args);
|
||||
private:
|
||||
bool KVLoadFromFile(KeyValues *kv, IFileSystem *filesystem, const char *resourceName, const char *pathID);
|
||||
private:
|
||||
IFileSystem* baseFs = nullptr;
|
||||
std::string sLastMap;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user