2016-07-26 15:18:32 +03:00
|
|
|
// vi: set ts=4 sw=4 :
|
|
|
|
// vim: set tw=75 :
|
|
|
|
|
|
|
|
// metamod.cpp - (main) implementation of metamod operations
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2001-2004 Will Day <willday@hpgx.net>
|
|
|
|
*
|
|
|
|
* This file is part of Metamod.
|
|
|
|
*
|
|
|
|
* Metamod is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* Metamod is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Metamod; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* In addition, as a special exception, the author gives permission to
|
|
|
|
* link the code of this program with the Half-Life Game g_engine ("HL
|
|
|
|
* g_engine") and Modified Game Libraries ("MODs") developed by Valve,
|
|
|
|
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
|
|
|
* respects for all of the code used other than the HL g_engine and MODs
|
|
|
|
* from Valve. If you modify this file, you may extend this exception
|
|
|
|
* to your version of the file, but you are not obligated to do so. If
|
|
|
|
* you do not wish to do so, delete this exception statement from your
|
|
|
|
* version.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-07-26 03:22:47 +03:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
cvar_t meta_version = {"metamod_version", VVERSION, FCVAR_SERVER , 0, NULL};
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
MConfig static_config;
|
2016-07-26 15:18:32 +03:00
|
|
|
MConfig* g_config = &static_config;
|
2016-07-04 09:07:29 +03:00
|
|
|
option_t global_options[] = {
|
2016-07-26 15:18:32 +03:00
|
|
|
{"debuglevel", CF_INT, &g_config->debuglevel, "0"},
|
|
|
|
{"gamedll", CF_PATH, &g_config->gamedll, NULL},
|
|
|
|
{"plugins_file", CF_PATH, &g_config->plugins_file, PLUGINS_INI},
|
|
|
|
{"exec_cfg", CF_STR, &g_config->exec_cfg, EXEC_CFG},
|
2016-07-04 09:07:29 +03:00
|
|
|
// list terminator
|
2016-07-26 15:18:32 +03:00
|
|
|
{NULL, CF_NONE, NULL, NULL}
|
2016-07-04 09:07:29 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
gamedll_t GameDLL;
|
2016-07-26 15:18:32 +03:00
|
|
|
|
2016-07-04 09:07:29 +03:00
|
|
|
meta_globals_t PublicMetaGlobals;
|
|
|
|
meta_globals_t PrivateMetaGlobals;
|
2016-07-26 15:18:32 +03:00
|
|
|
|
2016-07-04 09:07:29 +03:00
|
|
|
meta_enginefuncs_t g_plugin_engfuncs;
|
2016-07-26 15:18:32 +03:00
|
|
|
|
|
|
|
MPluginList* g_plugins;
|
|
|
|
MRegCmdList* g_regCmds;
|
|
|
|
MRegCvarList* g_regCvars;
|
|
|
|
MRegMsgList* g_regMsgs;
|
|
|
|
|
2016-07-26 03:22:47 +03:00
|
|
|
MPlayerList g_Players;
|
2016-07-26 15:18:32 +03:00
|
|
|
|
|
|
|
unsigned int CALL_API_count = 0;
|
|
|
|
|
2016-07-04 09:07:29 +03:00
|
|
|
int requestid_counter = 0;
|
2016-07-26 15:18:32 +03:00
|
|
|
|
|
|
|
#ifdef UNFINISHED
|
|
|
|
MHookList *Hooks;
|
|
|
|
#endif /* UNFINISHED */
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Very first metamod function that's run.
|
|
|
|
// Do startup operations...
|
2016-07-26 15:18:32 +03:00
|
|
|
void metamod_startup(void)
|
2016-07-26 03:22:47 +03:00
|
|
|
{
|
2016-07-26 15:18:32 +03:00
|
|
|
const char* mmfile = NULL;
|
|
|
|
const char* cfile = NULL;
|
|
|
|
const char* cp;
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
META_CONS(" ");
|
2016-07-26 15:18:32 +03:00
|
|
|
META_CONS(" %s version %s Copyright (c) 2001-%s %s", VNAME, VVERSION, COPYRIGHT_YEAR, VAUTHOR);
|
|
|
|
META_CONS(" %s comes with ABSOLUTELY NO WARRANTY; for details type `meta gpl'.", VNAME);
|
2016-07-04 09:07:29 +03:00
|
|
|
META_CONS(" This is free software, and you are welcome to redistribute it");
|
|
|
|
META_CONS(" under certain conditions; type `meta gpl' for details.");
|
|
|
|
META_CONS(" ");
|
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
META_LOG("%s v%s %s", VNAME, VVERSION, VDATE);
|
|
|
|
META_LOG("by %s", VAUTHOR);
|
|
|
|
META_LOG(" %s", VURL);
|
|
|
|
META_LOG("compiled: %s %s (%s)", COMPILE_TIME, COMPILE_TZONE, OPT_TYPE);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// If running with "+developer", allow an opportunity to break in with
|
|
|
|
// a debugger.
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((int) CVAR_GET_FLOAT("developer") != 0) {
|
|
|
|
//sleep(10); // TODO: WAT??????
|
|
|
|
}
|
|
|
|
|
|
|
|
// specify our new() handler
|
|
|
|
mm_set_new_handler();
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Get gamedir, very early on, because it seems we need it all over the
|
|
|
|
// place here at the start.
|
2016-07-26 15:18:32 +03:00
|
|
|
if (!meta_init_gamedll()) {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_ERROR("Failure to init game DLL; exiting...");
|
2016-07-26 15:18:32 +03:00
|
|
|
do_exit(1);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Register various console commands and cvars.
|
2016-07-26 15:18:32 +03:00
|
|
|
// Can I do these here, rather than waiting for GameDLLInit() ?
|
2016-07-04 09:07:29 +03:00
|
|
|
// Looks like it works okay..
|
|
|
|
meta_register_cmdcvar();
|
|
|
|
|
|
|
|
// Set a slight debug level for developer mode, if debug level not
|
|
|
|
// already set.
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((int) CVAR_GET_FLOAT("developer") != 0 && meta_debug.value == 0)
|
|
|
|
CVAR_SET_FLOAT("meta_debug", 3.0);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Init default values
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->init(global_options);
|
2016-07-04 09:07:29 +03:00
|
|
|
// Find config file
|
2016-07-26 15:18:32 +03:00
|
|
|
cfile = CONFIG_INI;
|
|
|
|
if ((cp = LOCALINFO("mm_configfile")) && *cp != '\0') {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Configfile specified via localinfo: %s", cp);
|
2016-07-26 03:22:47 +03:00
|
|
|
if (valid_gamedir_file(cp))
|
2016-07-26 15:18:32 +03:00
|
|
|
cfile = cp;
|
2016-07-04 09:07:29 +03:00
|
|
|
else
|
2016-07-26 15:18:32 +03:00
|
|
|
META_ERROR("Empty/missing config.ini file: %s; falling back to %s",
|
|
|
|
cp, cfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
// Load config file
|
2016-07-26 03:22:47 +03:00
|
|
|
if (valid_gamedir_file(cfile))
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->load(cfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
else
|
2016-07-26 15:18:32 +03:00
|
|
|
META_DEBUG(2, ("No config.ini file found: %s", CONFIG_INI));
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Now, override config options with localinfo commandline options.
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((cp = LOCALINFO("mm_debug")) && *cp != '\0') {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Debuglevel specified via localinfo: %s", cp);
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->set("debuglevel", cp);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((cp = LOCALINFO("mm_gamedll")) && *cp != '\0') {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Gamedll specified via localinfo: %s", cp);
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->set("gamedll", cp);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((cp = LOCALINFO("mm_pluginsfile")) && *cp != '\0') {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Pluginsfile specified via localinfo: %s", cp);
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->set("plugins_file", cp);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((cp = LOCALINFO("mm_execcfg")) && *cp != '\0') {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Execcfg specified via localinfo: %s", cp);
|
2016-07-26 15:18:32 +03:00
|
|
|
g_config->set("exec_cfg", cp);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check for an initial debug level, since cfg files don't get exec'd
|
|
|
|
// until later.
|
2016-07-26 15:18:32 +03:00
|
|
|
if (g_config->debuglevel != 0)
|
|
|
|
CVAR_SET_FLOAT("meta_debug", g_config->debuglevel);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Prepare for registered commands from plugins.
|
2016-07-26 15:18:32 +03:00
|
|
|
g_regCmds = new MRegCmdList();
|
|
|
|
g_regCvars = new MRegCvarList();
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Prepare for registered user messages from gamedll.
|
2016-07-26 15:18:32 +03:00
|
|
|
g_regMsgs = new MRegMsgList();
|
2016-07-26 03:22:47 +03:00
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
// Copy, and store pointer in g_engine struct. Yes, we could just store
|
|
|
|
// the actual engine_t struct in g_engine, but then it wouldn't be a
|
2016-07-04 09:07:29 +03:00
|
|
|
// pointer to match the other g_engfuncs.
|
2016-07-26 15:18:32 +03:00
|
|
|
g_plugin_engfuncs.set_from(g_engine.funcs);
|
|
|
|
g_engine.pl_funcs = &g_plugin_engfuncs;
|
2016-07-04 09:07:29 +03:00
|
|
|
// substitute our special versions of various commands
|
2016-07-26 15:18:32 +03:00
|
|
|
g_engine.pl_funcs->pfnAddServerCommand = meta_AddServerCommand;
|
|
|
|
g_engine.pl_funcs->pfnCVarRegister = meta_CVarRegister;
|
|
|
|
g_engine.pl_funcs->pfnCvar_RegisterVariable = meta_CVarRegister;
|
|
|
|
g_engine.pl_funcs->pfnRegUserMsg = meta_RegUserMsg;
|
|
|
|
if (g_engine.pl_funcs->pfnQueryClientCvarValue)
|
|
|
|
g_engine.pl_funcs->pfnQueryClientCvarValue = meta_QueryClientCvarValue;
|
|
|
|
|
|
|
|
#ifdef UNFINISHED
|
|
|
|
// Init the list of event/logline hooks.
|
|
|
|
Hooks = new MHookList();
|
|
|
|
#endif /* UNFINISHED */
|
2016-07-26 03:22:47 +03:00
|
|
|
|
2016-07-04 09:07:29 +03:00
|
|
|
// Before, we loaded plugins before loading the game DLL, so that if no
|
|
|
|
// plugins caught engine functions, we could pass engine funcs straight
|
|
|
|
// to game dll, rather than acting as intermediary. (Should perform
|
|
|
|
// better, right?)
|
|
|
|
//
|
|
|
|
// But since a plugin can be loaded at any time, we have to go ahead
|
|
|
|
// and catch the engine funcs regardless. Also, we want to give each
|
|
|
|
// plugin a copy of the gameDLL's api tables, in case they need to call
|
|
|
|
// API functions directly.
|
|
|
|
//
|
|
|
|
// Thus, load gameDLL first, then plugins.
|
|
|
|
//
|
2016-07-26 15:18:32 +03:00
|
|
|
// However, we have to init the g_plugins object first, because if the
|
2016-07-04 09:07:29 +03:00
|
|
|
// gamedll calls engine functions during GiveFnptrsToDll (like hpb_bot
|
|
|
|
// does) then it needs to be non-null so META_ENGINE_HANDLE won't crash.
|
|
|
|
//
|
|
|
|
// However, having replaced valid_file with valid_gamedir_file, we need
|
2016-07-26 15:18:32 +03:00
|
|
|
// to at least initialize the gameDLL to include the gamedir, before
|
2016-07-04 09:07:29 +03:00
|
|
|
// looking for plugins.ini.
|
|
|
|
//
|
|
|
|
// In fact, we need gamedir even earlier, so moved up above.
|
|
|
|
|
|
|
|
// Fall back to old plugins filename, if configured one isn't found.
|
2016-07-26 15:18:32 +03:00
|
|
|
mmfile = PLUGINS_INI;
|
2016-07-26 03:22:47 +03:00
|
|
|
if (!valid_gamedir_file(PLUGINS_INI) && valid_gamedir_file(OLD_PLUGINS_INI))
|
2016-07-26 15:18:32 +03:00
|
|
|
mmfile = OLD_PLUGINS_INI;
|
|
|
|
if (valid_gamedir_file(g_config->plugins_file))
|
|
|
|
mmfile = g_config->plugins_file;
|
2016-07-04 09:07:29 +03:00
|
|
|
else
|
2016-07-26 15:18:32 +03:00
|
|
|
META_ERROR("g_plugins file is empty/missing: %s; falling back to %s",
|
|
|
|
g_config->plugins_file, mmfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
g_plugins = new MPluginList(mmfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
2016-07-26 03:22:47 +03:00
|
|
|
if (!meta_load_gamedll()) {
|
2016-07-04 09:07:29 +03:00
|
|
|
META_ERROR("Failure to load game DLL; exiting...");
|
2016-07-26 15:18:32 +03:00
|
|
|
do_exit(1);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
if (!g_plugins->load()) {
|
|
|
|
META_ERROR("Failure to load plugins...");
|
2016-07-04 09:07:29 +03:00
|
|
|
// Exit on failure here? Dunno...
|
|
|
|
}
|
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
#ifdef UNFINISHED
|
|
|
|
// Start up the log parsing thread.
|
|
|
|
startup_logparse_thread();
|
|
|
|
#endif /* UNFINISHED */
|
|
|
|
|
2016-07-04 09:07:29 +03:00
|
|
|
// Allow for commands to metamod plugins at startup. Autoexec.cfg is
|
|
|
|
// read too early, and server.cfg is read too late.
|
|
|
|
//
|
|
|
|
// Only attempt load if the file appears to exist and be non-empty, to
|
|
|
|
// avoid confusing users with "couldn't exec exec.cfg" console
|
|
|
|
// messages.
|
2016-07-26 15:18:32 +03:00
|
|
|
if (valid_gamedir_file(g_config->exec_cfg))
|
|
|
|
mmfile = g_config->exec_cfg;
|
2016-07-26 03:22:47 +03:00
|
|
|
else if (valid_gamedir_file(OLD_EXEC_CFG))
|
2016-07-26 15:18:32 +03:00
|
|
|
mmfile = OLD_EXEC_CFG;
|
2016-07-04 09:07:29 +03:00
|
|
|
else
|
2016-07-26 15:18:32 +03:00
|
|
|
mmfile = NULL;
|
2016-07-26 03:22:47 +03:00
|
|
|
|
|
|
|
if (mmfile) {
|
2016-07-26 15:18:32 +03:00
|
|
|
if (mmfile[0] == '/')
|
|
|
|
META_ERROR("Cannot exec absolute pathnames: %s", mmfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
else {
|
2016-07-26 15:18:32 +03:00
|
|
|
char cmd[NAME_MAX ];
|
2016-07-04 09:07:29 +03:00
|
|
|
META_LOG("Exec'ing metamod exec.cfg: %s...", mmfile);
|
2016-07-26 15:18:32 +03:00
|
|
|
snprintf(cmd, sizeof(cmd), "exec %s\n", mmfile);
|
2016-07-04 09:07:29 +03:00
|
|
|
SERVER_COMMAND(cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set initial GameDLL fields (name, gamedir).
|
|
|
|
// meta_errno values:
|
2016-07-26 15:18:32 +03:00
|
|
|
// - ME_NULLRESULT _getcwd failed
|
|
|
|
mBOOL meta_init_gamedll(void)
|
|
|
|
{
|
|
|
|
char gamedir[PATH_MAX ];
|
|
|
|
char* cp;
|
2016-07-04 09:07:29 +03:00
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
memset(&GameDLL, 0, sizeof(GameDLL));
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
GET_GAME_DIR(gamedir);
|
|
|
|
normalize_pathname(gamedir);
|
|
|
|
//
|
|
|
|
// As of 1.1.1.1, the engine routine GET_GAME_DIR no longer returns a
|
|
|
|
// full-pathname, but rather just the path specified as the argument to
|
|
|
|
// "-game".
|
|
|
|
//
|
|
|
|
// However, since we have to work properly under both the new version
|
|
|
|
// as well as previous versions, we have to support both possibilities.
|
|
|
|
//
|
|
|
|
// Note: the code has always assumed the server op wouldn't do:
|
|
|
|
// hlds -game other/firearms
|
|
|
|
//
|
2016-07-26 15:18:32 +03:00
|
|
|
if (is_absolute_path(gamedir)) {
|
2016-07-04 09:07:29 +03:00
|
|
|
// Old style; GET_GAME_DIR returned full pathname. Copy this into
|
|
|
|
// our gamedir, and truncate to get the game name.
|
|
|
|
// (note check for both linux and win32 full pathname.)
|
2016-07-26 15:18:32 +03:00
|
|
|
strncpy(GameDLL.gamedir, gamedir, sizeof GameDLL.gamedir - 1);
|
|
|
|
GameDLL.gamedir[sizeof GameDLL.gamedir - 1] = '\0';
|
|
|
|
cp = strrchr(gamedir, '/') + 1;
|
|
|
|
strncpy(GameDLL.name, cp, sizeof GameDLL.name - 1);
|
|
|
|
GameDLL.name[sizeof GameDLL.name - 1] = '\0';
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
else {
|
2016-07-04 09:07:29 +03:00
|
|
|
// New style; GET_GAME_DIR returned game name. Copy this into our
|
|
|
|
// game name, and prepend the current working directory.
|
2016-07-26 15:18:32 +03:00
|
|
|
char buf[PATH_MAX ];
|
|
|
|
if (!_getcwd(buf, sizeof(buf))) {
|
|
|
|
META_ERROR("dll: Couldn't get cwd; %s", strerror(errno));
|
2016-07-04 09:07:29 +03:00
|
|
|
RETURN_ERRNO(mFALSE, ME_NULLRESULT);
|
|
|
|
}
|
2016-07-26 15:18:32 +03:00
|
|
|
snprintf(GameDLL.gamedir, sizeof(GameDLL.gamedir), "%s/%s", buf, gamedir);
|
|
|
|
strncpy(GameDLL.name, gamedir, sizeof GameDLL.name - 1);
|
|
|
|
GameDLL.name[sizeof GameDLL.name - 1] = '\0';
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
META_DEBUG(3, ("Game: %s", GameDLL.name));
|
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
return (mTRUE);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load game DLL.
|
|
|
|
// meta_errno values:
|
|
|
|
// - ME_DLOPEN couldn't dlopen game dll file
|
|
|
|
// - ME_DLMISSING couldn't find required routine in game dll
|
|
|
|
// (GiveFnptrsToDll, GetEntityAPI, GetEntityAPI2)
|
2016-07-26 15:18:32 +03:00
|
|
|
mBOOL meta_load_gamedll(void)
|
|
|
|
{
|
2016-07-04 09:07:29 +03:00
|
|
|
int iface_vers;
|
2016-07-26 15:18:32 +03:00
|
|
|
int found = 0;
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
GIVE_ENGINE_FUNCTIONS_FN pfn_give_engfuncs;
|
|
|
|
GETNEWDLLFUNCTIONS_FN pfn_getapinew;
|
|
|
|
GETENTITYAPI2_FN pfn_getapi2;
|
|
|
|
GETENTITYAPI_FN pfn_getapi;
|
|
|
|
|
2016-07-26 03:22:47 +03:00
|
|
|
if (!setup_gamedll(&GameDLL)) {
|
2016-07-26 15:18:32 +03:00
|
|
|
META_ERROR("dll: Unrecognized game: %s", GameDLL.name);
|
2016-07-04 09:07:29 +03:00
|
|
|
// meta_errno should be already set in lookup_game()
|
2016-07-26 15:18:32 +03:00
|
|
|
return (mFALSE);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// open the game DLL
|
2016-07-26 15:18:32 +03:00
|
|
|
if (!(GameDLL.handle = DLOPEN(GameDLL.pathname))) {
|
|
|
|
META_ERROR("dll: Couldn't load game DLL %s: %s", GameDLL.pathname,
|
|
|
|
DLERROR());
|
2016-07-04 09:07:29 +03:00
|
|
|
RETURN_ERRNO(mFALSE, ME_DLOPEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used to only pass our table of engine funcs if a loaded plugin
|
|
|
|
// wanted to catch one of the functions, but now that plugins are
|
|
|
|
// dynamically loadable at any time, we have to always pass our table,
|
|
|
|
// so that any plugin loaded later can catch what they need to.
|
2016-07-26 15:18:32 +03:00
|
|
|
if ((pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN) DLSYM(GameDLL.handle,
|
|
|
|
"GiveFnptrsToDll"))) {
|
|
|
|
pfn_give_engfuncs(&meta_engfuncs, gpGlobals);
|
|
|
|
META_DEBUG(3, ("dll: Game '%s': Called GiveFnptrsToDll",
|
|
|
|
GameDLL.name));
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
else {
|
2016-07-26 15:18:32 +03:00
|
|
|
META_ERROR("dll: Couldn't find GiveFnptrsToDll() in game DLL '%s': %s",
|
|
|
|
GameDLL.name, DLERROR());
|
2016-07-04 09:07:29 +03:00
|
|
|
RETURN_ERRNO(mFALSE, ME_DLMISSING);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Yes...another macro.
|
|
|
|
#define GET_FUNC_TABLE_FROM_GAME(gamedll, pfnGetFuncs, STR_GetFuncs, struct_field, API_TYPE, TABLE_TYPE, vers_pass, vers_int, vers_want, gotit) \
|
2016-07-26 15:18:32 +03:00
|
|
|
if((pfnGetFuncs = (API_TYPE) DLSYM(gamedll.handle, STR_GetFuncs))) { \
|
2016-07-04 09:07:29 +03:00
|
|
|
gamedll.funcs.struct_field = (TABLE_TYPE*) calloc(1, sizeof(TABLE_TYPE)); \
|
2016-07-26 15:18:32 +03:00
|
|
|
if(!gamedll.funcs.struct_field) {\
|
|
|
|
META_ERROR("malloc failed for gamedll struct_field: %s", STR_GetFuncs); \
|
2016-07-04 09:07:29 +03:00
|
|
|
} \
|
2016-07-26 15:18:32 +03:00
|
|
|
else if(pfnGetFuncs(gamedll.funcs.struct_field, vers_pass)) { \
|
2016-07-04 09:07:29 +03:00
|
|
|
META_DEBUG(3, ("dll: Game '%s': Found %s", gamedll.name, STR_GetFuncs)); \
|
|
|
|
gotit=1; \
|
|
|
|
} \
|
|
|
|
else { \
|
2016-07-26 15:18:32 +03:00
|
|
|
META_ERROR("dll: Failure calling %s in game '%s'", STR_GetFuncs, gamedll.name); \
|
2016-07-04 09:07:29 +03:00
|
|
|
free(gamedll.funcs.struct_field); \
|
|
|
|
gamedll.funcs.struct_field=NULL; \
|
2016-07-26 15:18:32 +03:00
|
|
|
if(vers_int != vers_want) { \
|
|
|
|
META_ERROR("dll: Interface version didn't match; we wanted %d, they had %d", vers_want, vers_int); \
|
2016-07-04 09:07:29 +03:00
|
|
|
/* reproduce error from engine */ \
|
|
|
|
META_CONS("=================="); \
|
|
|
|
META_CONS("Game DLL version mismatch"); \
|
|
|
|
META_CONS("DLL version is %d, engine version is %d", vers_int, vers_want); \
|
2016-07-26 15:18:32 +03:00
|
|
|
if(vers_int > vers_want) \
|
|
|
|
META_CONS("g_engine appears to be outdated, check for updates"); \
|
2016-07-04 09:07:29 +03:00
|
|
|
else \
|
|
|
|
META_CONS("The game DLL for %s appears to be outdated, check for updates", GameDLL.name); \
|
|
|
|
META_CONS("=================="); \
|
|
|
|
ALERT(at_error, "Exiting...\n"); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
else { \
|
|
|
|
META_DEBUG(5, ("dll: Game '%s': No %s", gamedll.name, STR_GetFuncs)); \
|
|
|
|
gamedll.funcs.struct_field=NULL; \
|
|
|
|
}
|
|
|
|
|
2016-07-26 15:18:32 +03:00
|
|
|
// Look for API-NEW interface in Game dll. We do this before API2/API, because
|
2016-07-04 09:07:29 +03:00
|
|
|
// that's what the engine appears to do..
|
2016-07-26 15:18:32 +03:00
|
|
|
iface_vers = NEW_DLL_FUNCTIONS_VERSION;
|
|
|
|
GET_FUNC_TABLE_FROM_GAME(GameDLL, pfn_getapinew, "GetNewDLLFunctions", newapi_table,
|
|
|
|
GETNEWDLLFUNCTIONS_FN, meta_new_dll_functions_t,
|
|
|
|
&iface_vers, iface_vers, NEW_DLL_FUNCTIONS_VERSION, found);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Look for API2 interface in plugin; preferred over API-1.
|
2016-07-26 15:18:32 +03:00
|
|
|
found = 0;
|
|
|
|
iface_vers = INTERFACE_VERSION;
|
|
|
|
GET_FUNC_TABLE_FROM_GAME(GameDLL, pfn_getapi2, "GetEntityAPI2", dllapi_table,
|
|
|
|
GETENTITYAPI2_FN, DLL_FUNCTIONS,
|
|
|
|
&iface_vers, iface_vers, INTERFACE_VERSION, found);
|
2016-07-04 09:07:29 +03:00
|
|
|
|
|
|
|
// Look for API-1 in plugin, if API2 interface wasn't found.
|
2016-07-26 15:18:32 +03:00
|
|
|
if (!found) {
|
2016-07-26 03:22:47 +03:00
|
|
|
found = 0;
|
2016-07-26 15:18:32 +03:00
|
|
|
GET_FUNC_TABLE_FROM_GAME(GameDLL, pfn_getapi, "GetEntityAPI", dllapi_table,
|
|
|
|
GETENTITYAPI_FN, DLL_FUNCTIONS,
|
|
|
|
INTERFACE_VERSION, INTERFACE_VERSION, INTERFACE_VERSION, found);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If didn't find either, return failure.
|
2016-07-26 15:18:32 +03:00
|
|
|
if (!found) {
|
|
|
|
META_ERROR("dll: Couldn't find either GetEntityAPI nor GetEntityAPI2 in game DLL '%s'", GameDLL.name);
|
2016-07-04 09:07:29 +03:00
|
|
|
RETURN_ERRNO(mFALSE, ME_DLMISSING);
|
|
|
|
}
|
|
|
|
|
|
|
|
META_LOG("Game DLL for '%s' loaded successfully", GameDLL.desc);
|
2016-07-26 15:18:32 +03:00
|
|
|
return (mTRUE);
|
2016-07-04 09:07:29 +03:00
|
|
|
}
|