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 "metamod_console.h"
|
||||||
#include "provider/provider_base.h"
|
#include "provider/provider_base.h"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#if SOURCE_ENGINE == SE_DOTA
|
|
||||||
#include <iserver.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define X64_SUFFIX ".x64"
|
#define X64_SUFFIX ".x64"
|
||||||
|
|
||||||
@ -50,50 +47,12 @@ using namespace SourceHook::Impl;
|
|||||||
* @file sourcemm.cpp
|
* @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
|
static void
|
||||||
InitializeVSP();
|
InitializeVSP();
|
||||||
|
|
||||||
|
static void
|
||||||
|
DoInitialPluginLoads();
|
||||||
|
|
||||||
static int
|
static int
|
||||||
LoadPluginsFromFile(const char *filepath, int &skipped);
|
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 */
|
/* Initialize everything here */
|
||||||
void
|
void
|
||||||
mm_InitializeForLoad()
|
mm_InitializeForLoad()
|
||||||
@ -184,46 +210,7 @@ mm_InitializeForLoad()
|
|||||||
*/
|
*/
|
||||||
in_first_level = true;
|
in_first_level = true;
|
||||||
|
|
||||||
#if SOURCE_ENGINE == SE_DOTA
|
provider->SetCallbacks(&s_ProviderCallbacks);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -531,174 +518,6 @@ mm_UnloadMetamod()
|
|||||||
g_SourceHook.CompleteShutdown();
|
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, ...)
|
void MetamodSource::LogMsg(ISmmPlugin *pl, const char *msg, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
@ -49,6 +49,29 @@ namespace SourceMM
|
|||||||
#endif
|
#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
|
* @brief Abstracts command information, since the new engine fixes the
|
||||||
* re-entrancy problems in the tokenization system.
|
* re-entrancy problems in the tokenization system.
|
||||||
@ -83,6 +106,13 @@ namespace SourceMM
|
|||||||
class IMetamodSourceProvider
|
class IMetamodSourceProvider
|
||||||
{
|
{
|
||||||
public:
|
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.
|
* @brief Returns whether source engine build is compatible.
|
||||||
*
|
*
|
||||||
@ -91,18 +121,6 @@ namespace SourceMM
|
|||||||
*/
|
*/
|
||||||
virtual bool IsSourceEngineBuildCompatible(int build) =0;
|
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.
|
* @brief Logs a message via IVEngineServer::LogPrint.
|
||||||
*
|
*
|
||||||
@ -167,6 +185,16 @@ namespace SourceMM
|
|||||||
*/
|
*/
|
||||||
virtual void DisplayWarning(const char *fmt, ...) =0;
|
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
|
* @brief Attempts to notify the provider of the gamedll version being
|
||||||
* used.
|
* used.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* vim: set ts=4 sw=4 tw=99 noet :
|
* vim: set ts=4 sw=4 tw=99 noet :
|
||||||
* ======================================================
|
* ======================================================
|
||||||
* Metamod:Source
|
* Metamod:Source
|
||||||
* Copyright (C) 2004-2009 AlliedModders LLC and authors.
|
* Copyright (C) 2004-2023 AlliedModders LLC and authors.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* ======================================================
|
* ======================================================
|
||||||
*
|
*
|
||||||
@ -30,8 +30,6 @@
|
|||||||
#include <convar.h>
|
#include <convar.h>
|
||||||
#include <eiface.h>
|
#include <eiface.h>
|
||||||
#include <tier0/icommandline.h>
|
#include <tier0/icommandline.h>
|
||||||
#include <sh_vector.h>
|
|
||||||
#include <sh_string.h>
|
|
||||||
#include "../metamod_util.h"
|
#include "../metamod_util.h"
|
||||||
#include "provider_base.h"
|
#include "provider_base.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
@ -39,31 +37,7 @@
|
|||||||
#include <filesystem.h>
|
#include <filesystem.h>
|
||||||
#include "metamod.h"
|
#include "metamod.h"
|
||||||
#include <tier1/KeyValues.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 */
|
/* Imports */
|
||||||
#if SOURCE_ENGINE < SE_ORANGEBOX
|
#if SOURCE_ENGINE < SE_ORANGEBOX
|
||||||
@ -71,239 +45,21 @@ struct UsrMsgInfo
|
|||||||
DLL_IMPORT ICommandLine *CommandLine();
|
DLL_IMPORT ICommandLine *CommandLine();
|
||||||
#endif
|
#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();
|
void _ServerCommand();
|
||||||
/* Variables */
|
/* Variables */
|
||||||
static BaseProvider g_Ep1Provider;
|
|
||||||
static List<ConCommandBase *> conbases_unreg;
|
static List<ConCommandBase *> conbases_unreg;
|
||||||
static CVector<UsrMsgInfo> usermsgs_list;
|
|
||||||
static jmp_buf usermsg_end;
|
|
||||||
static bool g_bOriginalEngine = false;
|
|
||||||
|
|
||||||
ICvar *icvar = NULL;
|
ICvar *icvar = NULL;
|
||||||
IFileSystem *baseFs = NULL;
|
|
||||||
IServerGameDLL *server = NULL;
|
IServerGameDLL *server = NULL;
|
||||||
#if SOURCE_ENGINE == SE_DOTA
|
|
||||||
static ISource2ServerConfig *serverconfig = NULL;
|
|
||||||
INetworkServerService *netservice = NULL;
|
|
||||||
IEngineServiceMgr *enginesvcmgr = NULL;
|
|
||||||
#endif
|
|
||||||
IVEngineServer *engine = NULL;
|
IVEngineServer *engine = NULL;
|
||||||
IServerGameClients *gameclients = NULL;
|
IServerGameClients *gameclients = NULL;
|
||||||
CGlobalVars *gpGlobals = 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()
|
bool BaseProvider::IsRemotePrintingAvailable()
|
||||||
{
|
{
|
||||||
return true;
|
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)
|
bool BaseProvider::IsSourceEngineBuildCompatible(int build)
|
||||||
{
|
{
|
||||||
return (build == SOURCE_ENGINE_ORIGINAL
|
return (build == SOURCE_ENGINE_ORIGINAL
|
||||||
@ -352,54 +108,6 @@ bool BaseProvider::LogMessage(const char *buffer)
|
|||||||
return true;
|
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, ...)
|
void BaseProvider::DisplayError(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@ -424,444 +132,14 @@ void BaseProvider::DisplayWarning(const char *fmt, ...)
|
|||||||
Warning("%s", buffer);
|
Warning("%s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
IConCommandBaseAccessor *BaseProvider::GetConCommandBaseAccessor()
|
void BaseProvider::DisplayDevMsg(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
return &g_SMConVarAccessor;
|
va_list ap;
|
||||||
}
|
char buffer[2048];
|
||||||
|
|
||||||
bool BaseProvider::RegisterConCommandBase(ConCommandBase *pCommand)
|
va_start(ap, fmt);
|
||||||
{
|
UTIL_FormatArgs(buffer, sizeof(buffer), fmt, ap);
|
||||||
return g_SMConVarAccessor.Register(pCommand);
|
va_end(ap);
|
||||||
}
|
|
||||||
|
DevMsg("%s", buffer);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -50,41 +50,47 @@ class INetworkGameServer;
|
|||||||
|
|
||||||
class BaseProvider : public IMetamodSourceProvider
|
class BaseProvider : public IMetamodSourceProvider
|
||||||
{
|
{
|
||||||
public:
|
public: // Must implement
|
||||||
virtual bool IsSourceEngineBuildCompatible(int build) override;
|
virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) override = 0;
|
||||||
virtual bool GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo) override;
|
virtual void Notify_DLLShutdown_Pre() override = 0;
|
||||||
virtual bool LogMessage(const char *buffer) override;
|
virtual int DetermineSourceEngine() override = 0;
|
||||||
virtual const char *GetCommandLineValue(const char *key, const char *defval) override;
|
virtual const char *GetEngineDescription() const override = 0;
|
||||||
virtual void ConsolePrint(const char *msg) override;
|
virtual void GetGamePath(char *pszBuffer, int len) override = 0;
|
||||||
virtual bool IsRemotePrintingAvailable() override;
|
virtual const char *GetGameDescription() override = 0;
|
||||||
virtual void ClientConsolePrint(edict_t *client, const char *msg) override;
|
virtual bool ProcessVDF(const char* file, char path[], size_t path_len, char alias[], size_t alias_len) override = 0;
|
||||||
virtual void DisplayError(const char *fmt, ...) override;
|
virtual void ConsolePrint(const char* msg) override = 0;
|
||||||
virtual void DisplayWarning(const char *fmt, ...) override;
|
virtual void ClientConsolePrint(edict_t* client, const char* msg) override = 0;
|
||||||
virtual int TryServerGameDLL(const char *iface) override;
|
virtual void ServerCommand(const char* cmd) override = 0;
|
||||||
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,
|
virtual ConVar* CreateConVar(const char* name,
|
||||||
const char* defval,
|
const char* defval,
|
||||||
const char* help,
|
const char* help,
|
||||||
int flags) override;
|
int flags) override = 0;
|
||||||
virtual const char *GetConVarString(ConVar *convar) override;
|
virtual const char* GetConVarString(ConVar* convar) override = 0;
|
||||||
virtual void SetConVarString(ConVar *convar, const char *str) override;
|
virtual void SetConVarString(ConVar* convar, const char* str) override = 0;
|
||||||
virtual void GetGamePath(char *pszBuffer, int len) override;
|
virtual IConCommandBaseAccessor* GetConCommandBaseAccessor() override = 0;
|
||||||
virtual const char *GetGameDescription() override;
|
virtual bool RegisterConCommandBase(ConCommandBase* pCommand) override = 0;
|
||||||
virtual IConCommandBaseAccessor *GetConCommandBaseAccessor() override;
|
virtual void UnregisterConCommandBase(ConCommandBase* pCommand) override = 0;
|
||||||
virtual bool RegisterConCommandBase(ConCommandBase *pCommand) override;
|
virtual bool IsConCommandBaseACommand(ConCommandBase* pCommand) override = 0;
|
||||||
virtual void UnregisterConCommandBase(ConCommandBase *pCommand) override;
|
public: // May implement/override (stubbed)
|
||||||
virtual bool IsConCommandBaseACommand(ConCommandBase *pCommand) override;
|
virtual int GetUserMessageCount() override { return -1; }
|
||||||
virtual int GetUserMessageCount() override;
|
virtual int FindUserMessage(const char *name, int *size=nullptr) override { return -1;}
|
||||||
virtual int FindUserMessage(const char *name, int *size=NULL) override;
|
virtual const char *GetUserMessage(int index, int *size=nullptr) override { return nullptr;}
|
||||||
virtual const char *GetUserMessage(int index, int *size=NULL) override;
|
public: // May implement/override
|
||||||
virtual int DetermineSourceEngine() override;
|
virtual bool IsSourceEngineBuildCompatible(int build) override;
|
||||||
virtual bool ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len) override;
|
virtual bool LogMessage(const char *buffer) override;
|
||||||
virtual const char *GetEngineDescription() const override;
|
virtual const char *GetCommandLineValue(const char *key, const char *defval) override;
|
||||||
#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 )
|
virtual bool IsRemotePrintingAvailable() override;
|
||||||
bool AllowDedicatedServers(EUniverse universe) const;
|
virtual void DisplayError(const char *fmt, ...) override;
|
||||||
#endif
|
virtual void DisplayWarning(const char *fmt, ...) override;
|
||||||
|
virtual void DisplayDevMsg(const char* fmt, ...) override;
|
||||||
|
virtual int TryServerGameDLL(const char *iface) override;
|
||||||
|
public:
|
||||||
|
void SetCallbacks(IMetamodSourceProviderCallbacks* pCallbacks) override final
|
||||||
|
{
|
||||||
|
m_pCallbacks = pCallbacks;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
IMetamodSourceProviderCallbacks* m_pCallbacks = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern IVEngineServer *engine;
|
extern IVEngineServer *engine;
|
||||||
|
@ -24,6 +24,625 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "provider_source.h"
|
#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;
|
static SourceProvider g_SourceProvider;
|
||||||
|
|
||||||
|
@ -29,10 +29,73 @@
|
|||||||
#define _INCLUDE_METAMOD_SOURCE_SOURCE1_PROVIDER_H_
|
#define _INCLUDE_METAMOD_SOURCE_SOURCE1_PROVIDER_H_
|
||||||
|
|
||||||
#include "../provider_base.h"
|
#include "../provider_base.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class SourceProvider : public BaseProvider
|
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
|
#endif
|
||||||
|
@ -24,6 +24,401 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "provider_source2.h"
|
#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;
|
static Source2Provider g_Source2Provider;
|
||||||
|
|
||||||
|
@ -29,10 +29,54 @@
|
|||||||
#define _INCLUDE_METAMOD_SOURCE_SOURCE2_PROVIDER_H_
|
#define _INCLUDE_METAMOD_SOURCE_SOURCE2_PROVIDER_H_
|
||||||
|
|
||||||
#include "../provider_base.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
|
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
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user