mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-12-04 16:24:16 +01:00
6cfff6ed91
--HG-- branch : sourcemm-1.6.0 rename : sourcemm/oslink.cpp => sourcemm/metamod_oslink.cpp rename : sourcemm/oslink.h => sourcemm/metamod_oslink.h extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/branches/sourcemm-1.6.0%40434
1205 lines
26 KiB
C++
1205 lines
26 KiB
C++
/* ======== SourceMM ========
|
|
* Copyright (C) 2004-2007 Metamod:Source Development Team
|
|
* No warranties of any kind
|
|
*
|
|
* License: zlib/libpng
|
|
*
|
|
* Author(s): David "BAILOPAN" Anderson
|
|
* Contributor(s): Scott "Damaged Soul" Ehlert
|
|
* : Pavol "PM OnoTo" Marko
|
|
* ============================
|
|
*/
|
|
|
|
#if defined _DEBUG
|
|
#define DEBUG2
|
|
#undef _DEBUG
|
|
#endif
|
|
#include <interface.h>
|
|
#include <eiface.h>
|
|
#include "metamod.h"
|
|
#include "metamod_provider.h"
|
|
#include "metamod_plugins.h"
|
|
#include "metamod_util.h"
|
|
#include "metamod_console.h"
|
|
#if defined DEBUG2
|
|
#undef DEBUG2
|
|
#define _DEBUG
|
|
#endif
|
|
|
|
using namespace SourceMM;
|
|
using namespace SourceHook;
|
|
|
|
/**
|
|
* @brief Implementation of main SourceMM GameDLL functionality
|
|
* @file sourcemm.cpp
|
|
*/
|
|
|
|
SH_DECL_MANUALHOOK4(SGD_DLLInit, 0, 0, 0, bool, CreateInterfaceFn, CreateInterfaceFn, CreateInterfaceFn, CGlobalVars *);
|
|
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);
|
|
SH_DECL_MANUALHOOK0_void(SGD_DLLShutdown, 0, 0, 0);
|
|
|
|
bool Handler_DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals);
|
|
bool Handler_DLLInit_Post(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals);
|
|
void Handler_DLLShutdown();
|
|
void Handler_LevelShutdown();
|
|
bool Handler_LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background);
|
|
bool Handler_GameInit();
|
|
void InitializeVSP();
|
|
|
|
struct game_dll_t
|
|
{
|
|
HINSTANCE lib;
|
|
CreateInterfaceFn factory;
|
|
};
|
|
|
|
SourceHook::CallClass<IServerGameDLL> *g_GameDllPatch;
|
|
String mod_path;
|
|
String metamod_path;
|
|
String full_bin_path;
|
|
bool parsed_game_info = false;
|
|
int vsp_version = 0;
|
|
int gamedll_version = 0;
|
|
int engine_build = SOURCE_ENGINE_UNKNOWN;
|
|
List<game_dll_t *> gamedll_list;
|
|
bool is_gamedll_loaded = false;
|
|
bool in_first_level = true;
|
|
bool is_game_init = false;
|
|
bool vsp_load_attempted = false;
|
|
bool vsp_load_requested = false;
|
|
bool vsp_loaded = false;
|
|
game_dll_t gamedll_info;
|
|
ConVar *metamod_version = NULL;
|
|
ConVar *mm_pluginsfile = NULL;
|
|
IServerGameDLL *server = NULL;
|
|
CreateInterfaceFn engine_factory = NULL;
|
|
CreateInterfaceFn physics_factory = NULL;
|
|
CreateInterfaceFn filesystem_factory = NULL;
|
|
CGlobalVars *gpGlobals = NULL;
|
|
SourceHook::CSourceHookImpl g_SourceHook;
|
|
SourceHook::ISourceHook *g_SHPtr = &g_SourceHook;
|
|
PluginId g_PLID = Pl_Console;
|
|
META_RES last_meta_res;
|
|
|
|
MetamodSource g_Metamod;
|
|
|
|
void ClearGamedllList();
|
|
|
|
/* Helper Macro */
|
|
#define IFACE_MACRO(orig,nam) \
|
|
CPluginManager::CPlugin *pl; \
|
|
SourceHook::List<IMetamodListener *>::iterator event; \
|
|
IMetamodListener *api; \
|
|
int mret = 0; \
|
|
void *val = NULL; \
|
|
for (PluginIter iter = g_PluginMngr._begin(); iter != g_PluginMngr._end(); iter++) { \
|
|
pl = (*iter); \
|
|
for (event=pl->m_Events.begin(); event!=pl->m_Events.end(); event++) { \
|
|
api = (*event); \
|
|
mret = IFACE_FAILED; \
|
|
if ( (val=api->On##nam##Query(iface, &mret)) != NULL ) { \
|
|
if (ret) *ret = mret; \
|
|
return val; \
|
|
} \
|
|
} \
|
|
} \
|
|
return (orig)(iface, ret);
|
|
|
|
#define ITER_EVENT(evn, args) \
|
|
CPluginManager::CPlugin *pl; \
|
|
SourceHook::List<IMetamodListener *>::iterator event; \
|
|
IMetamodListener *api; \
|
|
for (PluginIter iter = g_PluginMngr._begin(); iter != g_PluginMngr._end(); iter++) { \
|
|
pl = (*iter); \
|
|
for (event=pl->m_Events.begin(); event!=pl->m_Events.end(); event++) { \
|
|
api = (*event); \
|
|
api->evn args; \
|
|
} \
|
|
}
|
|
|
|
/* Initialize everything here */
|
|
void InitMainStates()
|
|
{
|
|
char full_path[PATH_SIZE] = {0};
|
|
GetFileOfAddress((void *)gamedll_info.factory, full_path, sizeof(full_path));
|
|
full_bin_path.assign(full_path);
|
|
|
|
/* Like Metamod, reload plugins at the end of the map.
|
|
* This is so plugins can hook everything on load, BUT, new plugins will be reloaded
|
|
* if the server is shut down (silly, but rare case).
|
|
*/
|
|
in_first_level = true;
|
|
|
|
SourceHook::MemFuncInfo info;
|
|
|
|
if (!provider->GetHookInfo(ProvidedHook_DLLInit, &info))
|
|
{
|
|
provider->DisplayError("Metamod:Source could not find a valid hook for IServerGameDLL::DLLInit");
|
|
}
|
|
SH_MANUALHOOK_RECONFIGURE(SGD_DLLInit, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
|
SH_ADD_MANUALHOOK_STATICFUNC(SGD_DLLInit, server, Handler_DLLInit, false);
|
|
SH_ADD_MANUALHOOK_STATICFUNC(SGD_DLLInit, server, Handler_DLLInit_Post, true);
|
|
|
|
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);
|
|
|
|
if (!provider->GetHookInfo(ProvidedHook_DLLShutdown, &info))
|
|
{
|
|
provider->DisplayError("Metamod:Source could not find a valid hook for IServerGameDLL::DLLShutdown");
|
|
}
|
|
SH_MANUALHOOK_RECONFIGURE(SGD_DLLShutdown, info.vtblindex, info.vtbloffs, info.thisptroffs);
|
|
SH_ADD_MANUALHOOK_STATICFUNC(SGD_DLLShutdown, server, Handler_DLLShutdown, false);
|
|
}
|
|
|
|
/* This is where the magic happens */
|
|
SMM_API void *CreateInterface(const char *iface, int *ret)
|
|
{
|
|
/* Prevent loading of self as a SourceMM plugin or Valve server plugin :x */
|
|
if (strcmp(iface, PLAPI_NAME) == 0)
|
|
{
|
|
provider->DisplayWarning("Do not try loading Metamod:Source as a plugin.\n");
|
|
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_FAILED;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (strncmp(iface, "ISERVERPLUGINCALLBACKS", 22) == 0)
|
|
{
|
|
IServerPluginCallbacks *pCallbacks = provider->GetVSPCallbacks(iface);
|
|
|
|
if (pCallbacks && vsp_version == 0)
|
|
{
|
|
vsp_version = atoi(&iface[22]);
|
|
}
|
|
|
|
if (pCallbacks && ret)
|
|
{
|
|
*ret = pCallbacks ? IFACE_OK : IFACE_FAILED;
|
|
}
|
|
|
|
return pCallbacks;
|
|
}
|
|
|
|
if (!parsed_game_info)
|
|
{
|
|
parsed_game_info = true;
|
|
const char *game_dir = NULL;
|
|
char game_path[PATH_SIZE];
|
|
char mm_path[PATH_SIZE];
|
|
|
|
/* Get path to SourceMM DLL */
|
|
if (!GetFileOfAddress((void *)CreateInterface, mm_path, sizeof(mm_path)))
|
|
{
|
|
provider->DisplayError("GetFileOfAddress() failed! Metamod cannot load.\n");
|
|
return NULL;
|
|
}
|
|
|
|
metamod_path.assign(mm_path);
|
|
|
|
/* Get value of -game from command line, defaulting to hl2 as engine seems to do */
|
|
game_dir = provider->GetCommandLineValue("-game", "hl2");
|
|
|
|
/* Get absolute path */
|
|
abspath(game_path, game_dir);
|
|
mod_path.assign(game_path);
|
|
|
|
char temp_path[PATH_SIZE];
|
|
|
|
/* Path to gameinfo.txt */
|
|
g_Metamod.PathFormat(temp_path, PATH_SIZE, "%s/%s", mod_path.c_str(), "gameinfo.txt");
|
|
|
|
FILE *fp = fopen(temp_path, "rt");
|
|
|
|
if (!fp)
|
|
{
|
|
provider->DisplayError("Unable to open gameinfo.txt! Metamod cannot load.\n");
|
|
return NULL;
|
|
}
|
|
|
|
char buffer[255];
|
|
char key[128], val[128];
|
|
bool search = false;
|
|
bool gamebin = false;
|
|
char *ptr;
|
|
const char *lptr;
|
|
char cur_path[PATH_SIZE];
|
|
|
|
getcwd(cur_path, PATH_SIZE);
|
|
|
|
while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL)
|
|
{
|
|
UTIL_TrimComments(buffer);
|
|
UTIL_TrimLeft(buffer);
|
|
UTIL_TrimRight(buffer);
|
|
|
|
if (stricmp(buffer, "SearchPaths") == 0)
|
|
{
|
|
search = true;
|
|
}
|
|
|
|
if (!search)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UTIL_KeySplit(buffer, key, sizeof(key) - 1, val, sizeof(val) - 1);
|
|
if (stricmp(key, "Game") == 0 || stricmp(key, "GameBin") == 0)
|
|
{
|
|
if (stricmp(key, "Game") == 0)
|
|
{
|
|
gamebin = false;
|
|
}
|
|
else
|
|
{
|
|
gamebin = true;
|
|
}
|
|
|
|
if (strncmp(val, "|gameinfo_path|", sizeof("|gameinfo_path|") - 1) == 0)
|
|
{
|
|
ptr = &(val[sizeof("|gameinfo_path|") - 1]);
|
|
if (ptr[0] == '.')
|
|
{
|
|
ptr++;
|
|
}
|
|
lptr = mod_path.c_str();
|
|
} else {
|
|
ptr = val;
|
|
lptr = cur_path;
|
|
}
|
|
|
|
size_t ptr_len = strlen(ptr);
|
|
if (ptr[ptr_len] == '/' || ptr[ptr_len] == '\\')
|
|
{
|
|
ptr[--ptr_len] = '\0';
|
|
}
|
|
|
|
/* No need to append "bin" if key is GameBin */
|
|
if (gamebin)
|
|
{
|
|
g_Metamod.PathFormat(temp_path, PATH_SIZE, "%s/%s/%s", lptr, ptr, SERVER_DLL);
|
|
}
|
|
else if (!ptr[0])
|
|
{
|
|
g_Metamod.PathFormat(temp_path, PATH_SIZE, "%s/%s/%s", lptr, "bin", SERVER_DLL);
|
|
}
|
|
else
|
|
{
|
|
g_Metamod.PathFormat(temp_path, PATH_SIZE, "%s/%s/%s/%s", lptr, ptr, "bin", SERVER_DLL);
|
|
}
|
|
|
|
/* If not path to SourceMM... */
|
|
if (!UTIL_PathCmp(mm_path, temp_path))
|
|
{
|
|
FILE *temp_fp = fopen(temp_path, "rb");
|
|
if (!temp_fp)
|
|
{
|
|
continue;
|
|
}
|
|
fclose(temp_fp);
|
|
|
|
/* Search for a matching game dll */
|
|
List<game_dll_t *>::iterator iter;
|
|
game_dll_t *pCheck;
|
|
bool found = false;
|
|
for (iter = gamedll_list.begin(); iter != gamedll_list.end(); iter++)
|
|
{
|
|
pCheck = (*iter);
|
|
if (GetFileOfAddress((void *)pCheck->factory, buffer, sizeof(buffer)))
|
|
{
|
|
if (UTIL_PathCmp(temp_path, buffer))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
HINSTANCE gamedll = dlmount(temp_path);
|
|
if (gamedll == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(gamedll, "CreateInterface");
|
|
if (fn == NULL)
|
|
{
|
|
dlclose(gamedll);
|
|
continue;
|
|
}
|
|
|
|
game_dll_t *pInfo = new game_dll_t;
|
|
pInfo->factory = fn;
|
|
pInfo->lib = gamedll;
|
|
gamedll_list.push_back(pInfo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
if (!is_gamedll_loaded)
|
|
{
|
|
if (strncmp(iface, "ServerGameDLL", 13) == 0)
|
|
{
|
|
List<game_dll_t *>::iterator iter;
|
|
game_dll_t *pInfo;
|
|
for (iter=gamedll_list.begin(); iter!=gamedll_list.end(); iter++)
|
|
{
|
|
pInfo = (*iter);
|
|
if ((server = (IServerGameDLL *)((pInfo->factory)(iface, ret))) != NULL)
|
|
{
|
|
if ((gamedll_version = provider->TryServerGameDLL(iface)) != 0)
|
|
{
|
|
/* We have a match. Erase us from the list and save our info. */
|
|
gamedll_list.erase(iter);
|
|
gamedll_info = *pInfo;
|
|
delete pInfo;
|
|
is_gamedll_loaded = true;
|
|
break;
|
|
}
|
|
/* :TODO: error otherwise? */
|
|
}
|
|
}
|
|
|
|
if (is_gamedll_loaded)
|
|
{
|
|
ClearGamedllList();
|
|
InitMainStates();
|
|
}
|
|
else
|
|
{
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_FAILED;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* wtf do we do... */
|
|
/* :TODO: .. something a bit more intelligent? */
|
|
provider->DisplayError("Engine requested unknown interface before GameDLL was known!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* If we got here, there's definitely a GameDLL */
|
|
IFACE_MACRO(gamedll_info.factory, GameDLL);
|
|
}
|
|
|
|
void ClearGamedllList()
|
|
{
|
|
List<game_dll_t *>::iterator iter;
|
|
|
|
game_dll_t *pInfo;
|
|
for (iter = gamedll_list.begin(); iter != gamedll_list.end(); iter++)
|
|
{
|
|
pInfo = (*iter);
|
|
dlclose(pInfo->lib);
|
|
delete pInfo;
|
|
}
|
|
|
|
gamedll_list.clear();
|
|
}
|
|
|
|
int LoadPluginsFromFile(const char *_file)
|
|
{
|
|
FILE *fp;
|
|
int total = 0, skipped=0;
|
|
PluginId id;
|
|
bool already;
|
|
|
|
fp = fopen(_file, "rt");
|
|
if (!fp)
|
|
{
|
|
LogMessage("[META] Could not open plugins file %s\n", _file);
|
|
return -1;
|
|
}
|
|
|
|
char buffer[255], error[255], full_path[255];
|
|
const char *ptr, *ext, *file;
|
|
size_t length;
|
|
while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL)
|
|
{
|
|
UTIL_TrimLeft(buffer);
|
|
UTIL_TrimRight(buffer);
|
|
|
|
length = strlen(buffer);
|
|
if (!length)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (buffer[0] == '\0' || buffer[0] == ';' || strncmp(buffer, "//", 2) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
file = buffer;
|
|
if (buffer[0] == '"')
|
|
{
|
|
char *cptr = buffer;
|
|
file = ++cptr;
|
|
|
|
while (*cptr)
|
|
{
|
|
if (*cptr == '"')
|
|
{
|
|
*cptr = '\0';
|
|
break;
|
|
}
|
|
cptr++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *cptr = buffer;
|
|
while (*cptr)
|
|
{
|
|
if (isspace(*cptr))
|
|
{
|
|
char *optr = cptr;
|
|
while (*cptr && isspace(*cptr))
|
|
{
|
|
cptr++;
|
|
}
|
|
*optr = '\0';
|
|
UTIL_TrimRight(cptr);
|
|
if (*cptr && isalpha(*cptr))
|
|
{
|
|
g_PluginMngr.SetAlias(buffer, cptr);
|
|
file = cptr;
|
|
}
|
|
break;
|
|
}
|
|
cptr++;
|
|
}
|
|
}
|
|
if (!file[0])
|
|
{
|
|
continue;
|
|
}
|
|
/* First find if it's an absolute path or not... */
|
|
if (file[0] == '/' || strncmp(&(file[1]), ":\\", 2) == 0)
|
|
{
|
|
/* If we're in an absolute path, ignore our normal heuristics */
|
|
id = g_PluginMngr.Load(file, Pl_File, already, error, sizeof(error));
|
|
if (id < Pl_MinId || g_PluginMngr.FindById(id)->m_Status < Pl_Paused)
|
|
{
|
|
LogMessage("[META] Failed to load plugin %s. %s", buffer, error);
|
|
}
|
|
else
|
|
{
|
|
if (already)
|
|
{
|
|
skipped++;
|
|
}
|
|
else
|
|
{
|
|
total++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Attempt to find a file extension */
|
|
ptr = UTIL_GetExtension(file);
|
|
/* Add an extension if there's none there */
|
|
if (!ptr)
|
|
{
|
|
#if defined WIN32 || defined _WIN32
|
|
ext = ".dll";
|
|
#else
|
|
ext = "_i486.so";
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ext = "";
|
|
}
|
|
/* Format the new path */
|
|
g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s%s", mod_path.c_str(), file, ext);
|
|
id = g_PluginMngr.Load(full_path, Pl_File, already, error, sizeof(error));
|
|
if (id < Pl_MinId || g_PluginMngr.FindById(id)->m_Status < Pl_Paused)
|
|
{
|
|
LogMessage("[META] Failed to load plugin %s. %s", buffer, error);
|
|
}
|
|
else
|
|
{
|
|
if (already)
|
|
{
|
|
skipped++;
|
|
}
|
|
else
|
|
{
|
|
total++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
|
|
if (skipped)
|
|
{
|
|
LogMessage("[META] Loaded %d plugins from file (%d already loaded)", total, skipped);
|
|
}
|
|
else
|
|
{
|
|
LogMessage("[META] Loaded %d plugins from file.", total);
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
void InitializeVSP()
|
|
{
|
|
size_t len;
|
|
char engine_file[PATH_SIZE];
|
|
char engine_path[PATH_SIZE];
|
|
char rel_path[PATH_SIZE * 2];
|
|
|
|
GetFileOfAddress((void *)engine_factory, engine_file, sizeof(engine_file));
|
|
|
|
/* Chop off the "engine" file part */
|
|
len = strlen(engine_file);
|
|
for (size_t i = len - 1; i >= 0 && i < len; i--)
|
|
{
|
|
if (engine_file[i] == '/' || engine_file[i] == '\\')
|
|
{
|
|
engine_file[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
abspath(engine_path, engine_file);
|
|
|
|
const char *usepath = metamod_path.c_str();
|
|
if (UTIL_Relatize(rel_path, sizeof(rel_path), engine_path, metamod_path.c_str()))
|
|
{
|
|
usepath = rel_path;
|
|
}
|
|
|
|
char command[PATH_SIZE * 2];
|
|
UTIL_Format(command, sizeof(command), "plugin_load \"%s\"\n", usepath);
|
|
provider->ServerCommand(command);
|
|
}
|
|
|
|
/* Wrapper function. This is called when the GameDLL thinks it's using
|
|
* the engine's real engineFactory.
|
|
*/
|
|
void *EngineFactory(const char *iface, int *ret)
|
|
{
|
|
IFACE_MACRO(engine_factory, Engine);
|
|
}
|
|
|
|
/* Wrapper function. This is called when the GameDLL thinks it's using
|
|
* the engine's real physicsFactory.
|
|
*/
|
|
void *PhysicsFactory(const char *iface, int *ret)
|
|
{
|
|
IFACE_MACRO(physics_factory, Physics);
|
|
}
|
|
|
|
/* Wrapper function. This is called when the GameDLL thinks it's using
|
|
* the engine's real fileSystemFactory.
|
|
*/
|
|
void *FileSystemFactory(const char *iface, int *ret)
|
|
{
|
|
IFACE_MACRO(filesystem_factory, FileSystem);
|
|
}
|
|
|
|
void LogMessage(const char *msg, ...)
|
|
{
|
|
va_list ap;
|
|
static char buffer[2048];
|
|
|
|
va_start(ap, msg);
|
|
size_t len = vsnprintf(buffer, sizeof(buffer) - 2, msg, ap);
|
|
va_end(ap);
|
|
|
|
buffer[len++] = '\n';
|
|
buffer[len] = '\0';
|
|
|
|
if (!provider->LogMessage(buffer))
|
|
{
|
|
fprintf(stdout, "%s", buffer);
|
|
}
|
|
}
|
|
|
|
bool Handler_DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
|
|
{
|
|
engine_factory = engineFactory;
|
|
filesystem_factory = filesystemFactory;
|
|
physics_factory = physicsFactory;
|
|
gpGlobals = pGlobals;
|
|
|
|
g_GameDllPatch = SH_GET_CALLCLASS(server);
|
|
|
|
provider->Notify_DLLInit_Pre(server, engineFactory, gamedll_info.factory);
|
|
|
|
metamod_version = provider->CreateConVar("metamod_version",
|
|
SOURCEMM_VERSION,
|
|
"Metamod:Source Version",
|
|
ConVarFlag_Notify|ConVarFlag_Replicated|ConVarFlag_SpOnly);
|
|
|
|
mm_pluginsfile = provider->CreateConVar("mm_pluginsfile",
|
|
#if defined WIN32 || defined _WIN32
|
|
"addons\\metamod\\metaplugins.ini",
|
|
#else
|
|
"addons/metamod/metaplugins.ini",
|
|
#endif
|
|
"Metamod:Source Plugins File",
|
|
ConVarFlag_SpOnly);
|
|
|
|
const char *pluginFile = provider->GetCommandLineValue("mm_pluginsfile", NULL);
|
|
if (!pluginFile)
|
|
{
|
|
pluginFile = provider->GetConVarString(mm_pluginsfile);
|
|
}
|
|
|
|
char full_path[260];
|
|
g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), pluginFile);
|
|
|
|
LoadPluginsFromFile(full_path);
|
|
|
|
in_first_level = true;
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, true);
|
|
}
|
|
|
|
bool Handler_GameInit()
|
|
{
|
|
if (is_game_init)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (vsp_load_requested)
|
|
{
|
|
InitializeVSP();
|
|
}
|
|
|
|
is_game_init = true;
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, true);
|
|
}
|
|
|
|
bool Handler_DLLInit_Post(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
|
|
{
|
|
g_PluginMngr.SetAllLoaded();
|
|
RETURN_META_VALUE(MRES_IGNORED, true);
|
|
}
|
|
|
|
void Handler_DLLShutdown()
|
|
{
|
|
/* Unload plugins */
|
|
g_PluginMngr.UnloadAll();
|
|
|
|
SH_CALL(g_GameDllPatch, &IServerGameDLL::DLLShutdown)();
|
|
|
|
SH_RELEASE_CALLCLASS(g_GameDllPatch);
|
|
g_GameDllPatch = NULL;
|
|
|
|
g_SourceHook.CompleteShutdown();
|
|
|
|
if (is_gamedll_loaded && gamedll_info.lib)
|
|
{
|
|
dlclose(gamedll_info.lib);
|
|
is_gamedll_loaded = false;
|
|
}
|
|
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
|
|
void Handler_LevelShutdown(void)
|
|
{
|
|
if (!in_first_level)
|
|
{
|
|
char full_path[255];
|
|
g_Metamod.PathFormat(full_path,
|
|
sizeof(full_path),
|
|
"%s/%s",
|
|
mod_path.c_str(),
|
|
provider->GetConVarString(mm_pluginsfile));
|
|
|
|
LoadPluginsFromFile(full_path);
|
|
}
|
|
else
|
|
{
|
|
in_first_level = false;
|
|
}
|
|
|
|
ITER_EVENT(OnLevelShutdown, ());
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void MetamodSource::LogMsg(ISmmPlugin *pl, const char *msg, ...)
|
|
{
|
|
va_list ap;
|
|
char buffer[2048];
|
|
|
|
va_start(ap, msg);
|
|
UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
|
|
va_end(ap);
|
|
|
|
LogMessage("[%s] %s", pl->GetLogTag(), buffer);
|
|
}
|
|
|
|
CreateInterfaceFn MetamodSource::engineFactory(bool syn/* =true */)
|
|
{
|
|
if (syn)
|
|
{
|
|
return EngineFactory;
|
|
}
|
|
|
|
return engine_factory;
|
|
}
|
|
|
|
CreateInterfaceFn MetamodSource::physicsFactory(bool syn/* =true */)
|
|
{
|
|
if (syn)
|
|
{
|
|
return PhysicsFactory;
|
|
}
|
|
|
|
return physics_factory;
|
|
}
|
|
|
|
CreateInterfaceFn MetamodSource::fileSystemFactory(bool syn/* =true */)
|
|
{
|
|
if (syn)
|
|
{
|
|
return FileSystemFactory;
|
|
}
|
|
|
|
return filesystem_factory;
|
|
}
|
|
|
|
CreateInterfaceFn MetamodSource::serverFactory(bool syn/* =true */)
|
|
{
|
|
if (syn)
|
|
{
|
|
return CreateInterface;
|
|
}
|
|
|
|
return gamedll_info.factory;
|
|
}
|
|
|
|
CGlobalVars *MetamodSource::pGlobals()
|
|
{
|
|
return gpGlobals;
|
|
}
|
|
|
|
void MetamodSource::SetLastMetaReturn(META_RES res)
|
|
{
|
|
last_meta_res = res;
|
|
}
|
|
|
|
META_RES MetamodSource::GetLastMetaReturn()
|
|
{
|
|
return last_meta_res;
|
|
}
|
|
|
|
void MetamodSource::ConPrint(const char *str)
|
|
{
|
|
provider->ConsolePrint(str);
|
|
}
|
|
|
|
void MetamodSource::ConPrintf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char buffer[2048];
|
|
|
|
va_start(ap, fmt);
|
|
UTIL_FormatArgs(buffer, sizeof(buffer), fmt, ap);
|
|
va_end(ap);
|
|
|
|
provider->ConsolePrint(buffer);
|
|
}
|
|
|
|
void MetamodSource::GetApiVersions(int &major, int &minor, int &plvers, int &plmin)
|
|
{
|
|
major = SM_VERS_API_MAJOR;
|
|
minor = SM_VERS_API_MINOR;
|
|
plvers = PLAPI_VERSION;
|
|
plmin = PLAPI_MIN_VERSION;
|
|
}
|
|
|
|
void MetamodSource::GetShVersions(int &shvers, int &shimpl)
|
|
{
|
|
shvers = SH_IFACE_VERSION;
|
|
shimpl = SH_IMPL_VERSION;
|
|
}
|
|
|
|
int MetamodSource::FormatIface(char iface[], unsigned int maxlength)
|
|
{
|
|
int length = (int)strlen(iface);
|
|
int i;
|
|
int num = 0;
|
|
|
|
for (i = length - 1; i >= 0; i--)
|
|
{
|
|
if (!isdigit(iface[i]))
|
|
{
|
|
if (i != length - 1)
|
|
{
|
|
num = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( (num && ((int)maxlength <= length)) || (!num && ((int)maxlength <= length + 3)) )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (i != length - 1)
|
|
{
|
|
num = atoi(&(iface[++i]));
|
|
}
|
|
|
|
num++;
|
|
|
|
snprintf(&(iface[i]), 4, "%03d", num);
|
|
|
|
return num;
|
|
}
|
|
|
|
void *MetamodSource::InterfaceSearch(CreateInterfaceFn fn, const char *iface, int max, int *ret)
|
|
{
|
|
char _if[256]; /* assume no interface goes beyond this */
|
|
size_t len = strlen(iface);
|
|
int num = 0;
|
|
void *pf = NULL;
|
|
|
|
if (max > 999)
|
|
{
|
|
max = 999;
|
|
}
|
|
|
|
if (len + 4 > sizeof(_if))
|
|
{
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_FAILED;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(_if, iface);
|
|
|
|
do
|
|
{
|
|
if ((pf = (fn)(_if, ret)) != NULL)
|
|
{
|
|
break;
|
|
}
|
|
if (num > max)
|
|
{
|
|
break;
|
|
}
|
|
} while ((num = FormatIface(_if, len+1)));
|
|
|
|
return pf;
|
|
}
|
|
|
|
void *MetamodSource::VInterfaceMatch(CreateInterfaceFn fn, const char *iface, int min)
|
|
{
|
|
char buffer[256]; /* assume no interface will go beyond this */
|
|
size_t len = strlen(iface);
|
|
int ret; /* just in case something doesn't handle NULL properly */
|
|
|
|
if (len > sizeof(buffer) - 4)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(buffer, iface);
|
|
|
|
if (min != -1)
|
|
{
|
|
char *ptr = &buffer[len - 1];
|
|
int digits = 0;
|
|
while (isdigit(*ptr) && digits <=3)
|
|
{
|
|
*ptr = '\0';
|
|
digits++;
|
|
ptr--;
|
|
}
|
|
if (digits != 3)
|
|
{
|
|
/* for now, assume this is an error */
|
|
strcpy(buffer, iface);
|
|
}
|
|
else
|
|
{
|
|
char num[4];
|
|
min = (min == 0) ? 1 : min;
|
|
snprintf(num, sizeof(num), "%03d", min);
|
|
strcat(buffer, num);
|
|
}
|
|
}
|
|
|
|
return InterfaceSearch(fn, buffer, IFACE_MAXNUM, &ret);
|
|
}
|
|
|
|
const char *MetamodSource::GetBaseDir()
|
|
{
|
|
return mod_path.c_str();
|
|
}
|
|
|
|
void MetamodSource::PathFormat(char *buffer, size_t len, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
size_t mylen = UTIL_FormatArgs(buffer, len, fmt, ap);
|
|
va_end(ap);
|
|
|
|
for (size_t i = 0; i < mylen; i++)
|
|
{
|
|
if (buffer[i] == ALT_SEP_CHAR)
|
|
{
|
|
buffer[i] = PATH_SEP_CHAR;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MetamodSource::ClientConPrintf(edict_t *client, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char buffer[2048];
|
|
|
|
va_start(ap, fmt);
|
|
UTIL_FormatArgs(buffer, sizeof(buffer), fmt, ap);
|
|
va_end(ap);
|
|
|
|
provider->ClientConsolePrint(client, buffer);
|
|
}
|
|
|
|
void MetamodSource::EnableVSPListener()
|
|
{
|
|
if (is_game_init && !vsp_load_requested && !vsp_loaded)
|
|
{
|
|
InitializeVSP();
|
|
}
|
|
|
|
vsp_load_requested = true;
|
|
}
|
|
|
|
int MetamodSource::GetVSPVersion()
|
|
{
|
|
return vsp_version;
|
|
}
|
|
|
|
int MetamodSource::GetGameDLLVersion()
|
|
{
|
|
return gamedll_version;
|
|
}
|
|
|
|
bool MetamodSource::RemotePrintingAvailable()
|
|
{
|
|
return provider->IsRemotePrintingAvailable();
|
|
}
|
|
|
|
void *MetamodSource::MetaFactory(const char *iface, int *ret, PluginId *id)
|
|
{
|
|
if (id)
|
|
{
|
|
*id = 0;
|
|
}
|
|
|
|
if (!iface)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* First check ours... we get first chance! */
|
|
if (strcmp(iface, MMIFACE_SOURCEHOOK) == 0)
|
|
{
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_OK;
|
|
}
|
|
return static_cast<void *>(static_cast<SourceHook::ISourceHook *>(&g_SourceHook));
|
|
}
|
|
else if (strcmp(iface, MMIFACE_PLMANAGER) == 0)
|
|
{
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_OK;
|
|
}
|
|
return static_cast<void *>(static_cast<ISmmPluginManager *>(&g_PluginMngr));
|
|
}
|
|
|
|
CPluginManager::CPlugin *pl;
|
|
List<IMetamodListener *>::iterator event;
|
|
IMetamodListener *api;
|
|
void *value;
|
|
|
|
int subret = 0;
|
|
for (PluginIter iter = g_PluginMngr._begin();
|
|
iter != g_PluginMngr._end();
|
|
iter++)
|
|
{
|
|
pl = (*iter);
|
|
for (event = pl->m_Events.begin(); event != pl->m_Events.end(); event++)
|
|
{
|
|
api = (*event);
|
|
subret = IFACE_FAILED;
|
|
if ((value = api->OnMetamodQuery(iface, &subret)) != NULL)
|
|
{
|
|
if (ret)
|
|
{
|
|
*ret = subret;
|
|
}
|
|
if (id)
|
|
{
|
|
*id = pl->m_Id;
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
{
|
|
*ret = IFACE_FAILED;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void MetamodSource::AddListener(ISmmPlugin *plugin, IMetamodListener *pListener)
|
|
{
|
|
CPluginManager::CPlugin *pl = g_PluginMngr.FindByAPI(plugin);
|
|
|
|
pl->m_Events.push_back(pListener);
|
|
}
|
|
|
|
const char *MetamodSource::GetGameBinaryPath()
|
|
{
|
|
return full_bin_path.c_str();
|
|
}
|
|
|
|
const char *MetamodSource::GetPluginsFile()
|
|
{
|
|
return provider->GetConVarString(mm_pluginsfile);
|
|
}
|
|
|
|
IConCommandBaseAccessor *MetamodSource::GetCvarBaseAccessor()
|
|
{
|
|
return provider->GetConCommandBaseAccessor();
|
|
}
|
|
|
|
bool MetamodSource::RegisterConCmdBase(ISmmPlugin *plugin, ConCommandBase *pCommand)
|
|
{
|
|
if (provider->IsConCommandBaseACommand(pCommand))
|
|
{
|
|
g_PluginMngr.AddPluginCmd(plugin, pCommand);
|
|
}
|
|
else
|
|
{
|
|
g_PluginMngr.AddPluginCvar(plugin, pCommand);
|
|
}
|
|
|
|
return provider->RegisterConCommandBase(pCommand);
|
|
}
|
|
|
|
void MetamodSource::UnregisterConCmdBase(ISmmPlugin *plugin, ConCommandBase *pCommand)
|
|
{
|
|
if (provider->IsConCommandBaseACommand(pCommand))
|
|
{
|
|
g_PluginMngr.RemovePluginCmd(plugin, pCommand);
|
|
}
|
|
else
|
|
{
|
|
g_PluginMngr.RemovePluginCvar(plugin, pCommand);
|
|
}
|
|
|
|
PluginIter iter;
|
|
CPluginManager::CPlugin *pPlugin;
|
|
List<IMetamodListener *>::iterator event;
|
|
IMetamodListener *pML;
|
|
CPluginManager::CPlugin *pOrig = g_PluginMngr.FindByAPI(plugin);
|
|
for (iter=g_PluginMngr._begin(); iter!=g_PluginMngr._end(); iter++)
|
|
{
|
|
pPlugin = (*iter);
|
|
if (pPlugin->m_Status < Pl_Paused)
|
|
{
|
|
continue;
|
|
}
|
|
/* Only valid for plugins >= 12 (v1:6, SourceMM 1.5) */
|
|
if (pPlugin->m_API->GetApiVersion() < 12)
|
|
{
|
|
continue;
|
|
}
|
|
for (event=pPlugin->m_Events.begin();
|
|
event!=pPlugin->m_Events.end();
|
|
event++)
|
|
{
|
|
pML = (*event);
|
|
pML->OnUnlinkConCommandBase(pOrig ? pOrig->m_Id : 0, pCommand);
|
|
}
|
|
}
|
|
|
|
return provider->UnregisterConCommandBase(pCommand);
|
|
}
|
|
|
|
int MetamodSource::GetUserMessageCount()
|
|
{
|
|
return provider->GetUserMessageCount();
|
|
}
|
|
|
|
int MetamodSource::FindUserMessage(const char *name, int *size/* =NULL */)
|
|
{
|
|
return provider->FindUserMessage(name, size);
|
|
}
|
|
|
|
const char *MetamodSource::GetUserMessage(int index, int *size/* =NULL */)
|
|
{
|
|
return provider->GetUserMessage(index, size);
|
|
}
|
|
|
|
int MetamodSource::GetSourceEngineBuild()
|
|
{
|
|
return engine_build;
|
|
}
|