65df386d66
Modules are loaded before plugins, and can hook OnPluginsLoaded. Therefore, they should be unloaded AFTER plugins, and be able to hook OnPluginsUnloaded. This was not the case for reloadable modules. This affects nvault module, whose OnPluginsUnloaded function wasn't being called.
1878 lines
50 KiB
C++
Executable File
1878 lines
50 KiB
C++
Executable File
// vim: set ts=4 sw=4 tw=99 noet:
|
|
//
|
|
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
|
|
// Copyright (C) The AMX Mod X Development Team.
|
|
//
|
|
// This software is licensed under the GNU General Public License, version 3 or higher.
|
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
|
// https://alliedmods.net/amxmodx-license
|
|
|
|
#include <time.h>
|
|
#include "amxmodx.h"
|
|
#include "fakemeta.h"
|
|
#include "CMenu.h"
|
|
#include "newmenus.h"
|
|
#include "natives.h"
|
|
#include "binlog.h"
|
|
#include "optimizer.h"
|
|
#include "libraries.h"
|
|
#include "messages.h"
|
|
|
|
#include "datastructs.h"
|
|
#include "CFlagManager.h"
|
|
#include <amxmodx_version.h>
|
|
#include "trie_natives.h"
|
|
#include "CDataPack.h"
|
|
#include "textparse.h"
|
|
#include "CvarManager.h"
|
|
#include "CLibrarySys.h"
|
|
#include "CFileSystem.h"
|
|
#include "gameconfigs.h"
|
|
#include "CGameConfigs.h"
|
|
#include <engine_strucs.h>
|
|
#include <CDetour/detours.h>
|
|
#include "CoreConfig.h"
|
|
#include <resdk/mod_rehlds_api.h>
|
|
#include <amtl/am-utility.h>
|
|
|
|
plugin_info_t Plugin_info =
|
|
{
|
|
META_INTERFACE_VERSION, // ifvers
|
|
"AMX Mod X", // name
|
|
AMXX_VERSION, // version
|
|
__DATE__, // date
|
|
"AMX Mod X Dev Team", // author
|
|
"http://www.amxmodx.org", // url
|
|
"AMXX", // logtag
|
|
PT_STARTUP, // (when) loadable
|
|
PT_ANYTIME, // (when) unloadable
|
|
};
|
|
|
|
meta_globals_t *gpMetaGlobals;
|
|
gamedll_funcs_t *gpGamedllFuncs;
|
|
mutil_funcs_t *gpMetaUtilFuncs;
|
|
enginefuncs_t g_engfuncs;
|
|
globalvars_t *gpGlobals;
|
|
|
|
funEventCall modMsgsEnd[MAX_REG_MSGS];
|
|
funEventCall modMsgs[MAX_REG_MSGS];
|
|
|
|
void (*function)(void*);
|
|
void (*endfunction)(void*);
|
|
|
|
extern List<AUTHORIZEFUNC> g_auth_funcs;
|
|
extern ke::Vector<CAdminData *> DynamicAdmins;
|
|
|
|
CLog g_log;
|
|
CForwardMngr g_forwards;
|
|
ke::Vector<ke::AutoPtr<CPlayer *>> g_auth;
|
|
ke::Vector<ke::AutoPtr<ForceObject>> g_forcemodels;
|
|
ke::Vector<ke::AutoPtr<ForceObject>> g_forcesounds;
|
|
ke::Vector<ke::AutoPtr<ForceObject>> g_forcegeneric;
|
|
CPlayer g_players[33];
|
|
CPlayer* mPlayer;
|
|
CPluginMngr g_plugins;
|
|
CTaskMngr g_tasksMngr;
|
|
CFrameActionMngr g_frameActionMngr;
|
|
CmdMngr g_commands;
|
|
CFlagManager FlagMan;
|
|
EventsMngr g_events;
|
|
Grenades g_grenades;
|
|
LogEventsMngr g_logevents;
|
|
MenuMngr g_menucmds;
|
|
CLangMngr g_langMngr;
|
|
ke::AString g_log_dir;
|
|
ke::AString g_mod_name;
|
|
XVars g_xvars;
|
|
|
|
bool g_bmod_tfc;
|
|
bool g_bmod_cstrike;
|
|
bool g_bmod_dod;
|
|
bool g_dontprecache;
|
|
bool g_forcedmodules;
|
|
bool g_forcedsounds;
|
|
|
|
fakecmd_t g_fakecmd;
|
|
|
|
float g_game_restarting;
|
|
float g_game_timeleft;
|
|
float g_task_time;
|
|
float g_auth_time;
|
|
|
|
bool g_initialized = false;
|
|
bool g_coloredmenus;
|
|
bool g_activated = false;
|
|
bool g_NewDLL_Available = false;
|
|
|
|
#ifdef MEMORY_TEST
|
|
float g_next_memreport_time;
|
|
unsigned int g_memreport_count;
|
|
ke::AString g_memreport_dir;
|
|
bool g_memreport_enabled;
|
|
#define MEMREPORT_INTERVAL 300.0f /* 5 mins */
|
|
#endif // MEMORY_TEST
|
|
|
|
hudtextparms_t g_hudset;
|
|
//int g_edict_point;
|
|
int g_players_num;
|
|
int mPlayerIndex;
|
|
int mState;
|
|
int g_srvindex;
|
|
|
|
CDetour *DropClientDetour;
|
|
bool g_isDropClientHookEnabled = false;
|
|
bool g_isDropClientHookAvailable = false;
|
|
void SV_DropClient_RH(IRehldsHook_SV_DropClient *chain, IGameClient *cl, bool crash, const char *format);
|
|
|
|
cvar_t init_amxmodx_version = {"amxmodx_version", "", FCVAR_SERVER | FCVAR_SPONLY};
|
|
cvar_t init_amxmodx_modules = {"amxmodx_modules", "", FCVAR_SPONLY};
|
|
cvar_t init_amxmodx_debug = {"amx_debug", "1", FCVAR_SPONLY};
|
|
cvar_t init_amxmodx_mldebug = {"amx_mldebug", "", FCVAR_SPONLY};
|
|
cvar_t init_amxmodx_language = {"amx_language", "en", FCVAR_SERVER};
|
|
cvar_t init_amxmodx_cl_langs = {"amx_client_languages", "1", FCVAR_SERVER};
|
|
cvar_t* amxmodx_version = NULL;
|
|
cvar_t* amxmodx_modules = NULL;
|
|
cvar_t* amxmodx_language = NULL;
|
|
cvar_t* hostname = NULL;
|
|
cvar_t* mp_timelimit = NULL;
|
|
|
|
// main forwards
|
|
int FF_ClientCommand = -1;
|
|
int FF_ClientConnect = -1;
|
|
int FF_ClientDisconnect = -1;
|
|
int FF_ClientDisconnected = -1;
|
|
int FF_ClientRemove = -1;
|
|
int FF_ClientInfoChanged = -1;
|
|
int FF_ClientPutInServer = -1;
|
|
int FF_PluginInit = -1;
|
|
int FF_PluginCfg = -1;
|
|
int FF_PluginPrecache = -1;
|
|
int FF_PluginLog = -1;
|
|
int FF_PluginEnd = -1;
|
|
int FF_InconsistentFile = -1;
|
|
int FF_ClientAuthorized = -1;
|
|
int FF_ChangeLevel = -1;
|
|
int FF_ClientConnectEx = -1;
|
|
|
|
IFileSystem* g_FileSystem;
|
|
HLTypeConversion TypeConversion;
|
|
|
|
bool ColoredMenus(const char *ModName)
|
|
{
|
|
const char * pModNames[] = { "cstrike", "czero", "dmc", "dod", "tfc", "valve" };
|
|
const size_t ModsCount = sizeof(pModNames) / sizeof(const char *);
|
|
|
|
for (size_t i = 0; i < ModsCount; ++i)
|
|
{
|
|
if (strcmp(ModName, pModNames[i]) == 0)
|
|
return true; // this game modification currently supports colored menus
|
|
}
|
|
|
|
return false; // no colored menus are supported for this game modification
|
|
}
|
|
|
|
void ParseAndOrAdd(CStack<ke::AString *> & files, const char *name)
|
|
{
|
|
if (strncmp(name, "plugins-", 8) == 0)
|
|
{
|
|
#if !defined WIN32
|
|
size_t len = strlen(name);
|
|
if (strcmp(&name[len-4], ".ini") == 0)
|
|
{
|
|
#endif
|
|
ke::AString *pString = new ke::AString(name);
|
|
files.push(pString);
|
|
#if !defined WIN32
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void BuildPluginFileList(const char *initialdir, CStack<ke::AString *> & files)
|
|
{
|
|
char path[PLATFORM_MAX_PATH];
|
|
#if defined WIN32
|
|
build_pathname_r(path, sizeof(path), "%s/*.ini", initialdir);
|
|
_finddata_t fd;
|
|
intptr_t handle = _findfirst(path, &fd);
|
|
|
|
if (handle < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (!_findnext(handle, &fd))
|
|
{
|
|
ParseAndOrAdd(files, fd.name);
|
|
}
|
|
|
|
_findclose(handle);
|
|
#elif defined(__linux__) || defined(__APPLE__)
|
|
build_pathname_r(path, sizeof(path), "%s/", initialdir);
|
|
struct dirent *ep;
|
|
DIR *dp;
|
|
|
|
if ((dp = opendir(path)) == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while ( (ep=readdir(dp)) != NULL )
|
|
{
|
|
ParseAndOrAdd(files, ep->d_name);
|
|
}
|
|
|
|
closedir (dp);
|
|
#endif
|
|
}
|
|
|
|
//Loads a plugin list into the Plugin Cache and Load Modules cache
|
|
void LoadExtraPluginsToPCALM(const char *initialdir)
|
|
{
|
|
CStack<ke::AString *> files;
|
|
BuildPluginFileList(initialdir, files);
|
|
char path[255];
|
|
while (!files.empty())
|
|
{
|
|
ke::AString *pString = files.front();
|
|
ke::SafeSprintf(path, sizeof(path), "%s/%s",
|
|
initialdir,
|
|
pString->chars());
|
|
g_plugins.CALMFromFile(path);
|
|
delete pString;
|
|
files.pop();
|
|
}
|
|
}
|
|
|
|
void LoadExtraPluginsFromDir(const char *initialdir)
|
|
{
|
|
CStack<ke::AString *> files;
|
|
char path[255];
|
|
BuildPluginFileList(initialdir, files);
|
|
while (!files.empty())
|
|
{
|
|
ke::AString *pString = files.front();
|
|
ke::SafeSprintf(path, sizeof(path), "%s/%s",
|
|
initialdir,
|
|
pString->chars());
|
|
g_plugins.loadPluginsFromFile(path);
|
|
delete pString;
|
|
files.pop();
|
|
}
|
|
}
|
|
|
|
// Precache stuff from force consistency calls
|
|
// or check for pointed files won't be done
|
|
int C_PrecacheModel(const char *s)
|
|
{
|
|
if (!g_forcedmodules)
|
|
{
|
|
g_forcedmodules = true;
|
|
for (auto &model : g_forcemodels)
|
|
{
|
|
PRECACHE_MODEL(model->getFilename());
|
|
ENGINE_FORCE_UNMODIFIED(model->getForceType(), model->getMin(), model->getMax(), model->getFilename());
|
|
}
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
int C_PrecacheSound(const char *s)
|
|
{
|
|
if (!g_forcedsounds)
|
|
{
|
|
g_forcedsounds = true;
|
|
for (auto &sound : g_forcesounds)
|
|
{
|
|
PRECACHE_SOUND(sound->getFilename());
|
|
ENGINE_FORCE_UNMODIFIED(sound->getForceType(), sound->getMin(), sound->getMax(), sound->getFilename());
|
|
}
|
|
|
|
if (!g_bmod_cstrike)
|
|
{
|
|
PRECACHE_SOUND("weapons/cbar_hitbod1.wav");
|
|
PRECACHE_SOUND("weapons/cbar_hitbod2.wav");
|
|
PRECACHE_SOUND("weapons/cbar_hitbod3.wav");
|
|
}
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
// On InconsistentFile call forward function from plugins
|
|
int C_InconsistentFile(const edict_t *player, const char *filename, char *disconnect_message)
|
|
{
|
|
if (FF_InconsistentFile < 0)
|
|
RETURN_META_VALUE(MRES_IGNORED, FALSE);
|
|
|
|
if (MDLL_InconsistentFile(player, filename, disconnect_message))
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER((edict_t *)player);
|
|
|
|
if (executeForwards(FF_InconsistentFile, static_cast<cell>(pPlayer->index),
|
|
filename, disconnect_message) == 1)
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, FALSE);
|
|
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, TRUE);
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, FALSE);
|
|
}
|
|
|
|
const char* get_localinfo(const char* name, const char* def)
|
|
{
|
|
const char* b = LOCALINFO((char*)name);
|
|
|
|
if (b == 0 || *b == 0)
|
|
{
|
|
SET_LOCALINFO((char*)name, (char*)(b = def));
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
const char* get_localinfo_r(const char *name, const char *def, char buffer[], size_t maxlength)
|
|
{
|
|
const char* b = LOCALINFO((char*)name);
|
|
|
|
if (b == 0 || *b == 0)
|
|
{
|
|
SET_LOCALINFO((char*)name, (char*)(b = def));
|
|
}
|
|
|
|
ke::SafeSprintf(buffer, maxlength, "%s", b);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// Very first point at map load
|
|
// Load AMX modules for new native functions
|
|
// Initialize AMX stuff and load it's plugins from plugins.ini list
|
|
// Call precache forward function from plugins
|
|
int C_Spawn(edict_t *pent)
|
|
{
|
|
if (g_initialized)
|
|
{
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
g_activated = false;
|
|
g_initialized = true;
|
|
g_forcedmodules = false;
|
|
g_forcedsounds = false;
|
|
|
|
g_srvindex = IS_DEDICATED_SERVER() ? 0 : 1;
|
|
|
|
hostname = CVAR_GET_POINTER("hostname");
|
|
mp_timelimit = CVAR_GET_POINTER("mp_timelimit");
|
|
|
|
// Fix for crashing on mods that do not have mp_timelimit
|
|
if (mp_timelimit == NULL)
|
|
{
|
|
static cvar_t timelimit_holder;
|
|
|
|
timelimit_holder.name = "mp_timelimit";
|
|
timelimit_holder.string = "0";
|
|
timelimit_holder.flags = 0;
|
|
timelimit_holder.value = 0.0;
|
|
|
|
CVAR_REGISTER(&timelimit_holder);
|
|
|
|
mp_timelimit = &timelimit_holder;
|
|
|
|
}
|
|
|
|
g_forwards.clear();
|
|
|
|
g_log.MapChange();
|
|
|
|
// ###### Initialize task manager
|
|
g_tasksMngr.registerTimers(&gpGlobals->time, &mp_timelimit->value, &g_game_timeleft);
|
|
|
|
// ###### Initialize commands prefixes
|
|
g_commands.registerPrefix("amx");
|
|
g_commands.registerPrefix("amxx");
|
|
g_commands.registerPrefix("say");
|
|
g_commands.registerPrefix("admin_");
|
|
g_commands.registerPrefix("sm_");
|
|
g_commands.registerPrefix("cm_");
|
|
|
|
// make sure localinfos are set
|
|
get_localinfo("amxx_basedir", "addons/amxmodx");
|
|
get_localinfo("amxx_pluginsdir", "addons/amxmodx/plugins");
|
|
get_localinfo("amxx_modulesdir", "addons/amxmodx/modules");
|
|
get_localinfo("amxx_configsdir", "addons/amxmodx/configs");
|
|
get_localinfo("amxx_customdir", "addons/amxmodx/custom");
|
|
|
|
// make sure bcompat localinfos are set
|
|
get_localinfo("amx_basedir", "addons/amxmodx");
|
|
get_localinfo("amx_configdir", "addons/amxmodx/configs");
|
|
get_localinfo("amx_langdir", "addons/amxmodx/data/amxmod-lang");
|
|
get_localinfo("amx_modulesdir", "addons/amxmodx/modules");
|
|
get_localinfo("amx_pluginsdir", "addons/amxmodx/plugins");
|
|
get_localinfo("amx_logdir", "addons/amxmodx/logs");
|
|
|
|
FlagMan.LoadFile();
|
|
|
|
ArrayHandles.clear();
|
|
TrieHandles.clear();
|
|
TrieIterHandles.clear();
|
|
TrieSnapshotHandles.clear();
|
|
DataPackHandles.clear();
|
|
TextParsersHandles.clear();
|
|
GameConfigHandle.clear();
|
|
|
|
char map_pluginsfile_path[256];
|
|
char prefixed_map_pluginsfile[256];
|
|
char configs_dir[256];
|
|
|
|
// ###### Load modules
|
|
loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), PT_ANYTIME);
|
|
|
|
get_localinfo_r("amxx_configsdir", "addons/amxmodx/configs", configs_dir, sizeof(configs_dir)-1);
|
|
g_plugins.CALMFromFile(get_localinfo("amxx_plugins", "addons/amxmodx/configs/plugins.ini"));
|
|
LoadExtraPluginsToPCALM(configs_dir);
|
|
char temporaryMap[64], *tmap_ptr;
|
|
|
|
ke::SafeSprintf(temporaryMap, sizeof(temporaryMap), "%s", STRING(gpGlobals->mapname));
|
|
|
|
prefixed_map_pluginsfile[0] = '\0';
|
|
if ((tmap_ptr = strchr(temporaryMap, '_')) != NULL)
|
|
{
|
|
// this map has a prefix
|
|
|
|
*tmap_ptr = '\0';
|
|
ke::SafeSprintf(prefixed_map_pluginsfile,
|
|
sizeof(prefixed_map_pluginsfile),
|
|
"%s/maps/plugins-%s.ini",
|
|
configs_dir,
|
|
temporaryMap);
|
|
g_plugins.CALMFromFile(prefixed_map_pluginsfile);
|
|
}
|
|
|
|
ke::SafeSprintf(map_pluginsfile_path,
|
|
sizeof(map_pluginsfile_path),
|
|
"%s/maps/plugins-%s.ini",
|
|
configs_dir,
|
|
STRING(gpGlobals->mapname));
|
|
g_plugins.CALMFromFile(map_pluginsfile_path);
|
|
|
|
int loaded = countModules(CountModules_Running); // Call after attachModules so all modules don't have pending stat
|
|
|
|
// Set some info about amx version and modules
|
|
CVAR_SET_STRING(init_amxmodx_version.name, AMXX_VERSION);
|
|
char buffer[32];
|
|
sprintf(buffer, "%d", loaded);
|
|
CVAR_SET_STRING(init_amxmodx_modules.name, buffer);
|
|
|
|
// ###### Load Vault
|
|
char file[PLATFORM_MAX_PATH];
|
|
g_vault.setSource(build_pathname_r(file, sizeof(file), "%s", get_localinfo("amxx_vault", "addons/amxmodx/configs/vault.ini")));
|
|
g_vault.loadVault();
|
|
|
|
// ###### Init time and freeze tasks
|
|
g_game_timeleft = g_bmod_dod ? 1.0f : 0.0f;
|
|
g_task_time = gpGlobals->time + 99999.0f;
|
|
g_auth_time = gpGlobals->time + 99999.0f;
|
|
#ifdef MEMORY_TEST
|
|
g_next_memreport_time = gpGlobals->time + 99999.0f;
|
|
#endif
|
|
g_players_num = 0;
|
|
|
|
// Set server flags
|
|
memset(g_players[0].flags, -1, sizeof(g_players[0].flags));
|
|
|
|
g_opt_level = atoi(get_localinfo("optimizer", "7"));
|
|
if (!g_opt_level)
|
|
g_opt_level = 7;
|
|
|
|
// ###### Load AMX Mod X plugins
|
|
g_plugins.loadPluginsFromFile(get_localinfo("amxx_plugins", "addons/amxmodx/configs/plugins.ini"));
|
|
LoadExtraPluginsFromDir(configs_dir);
|
|
g_plugins.loadPluginsFromFile(map_pluginsfile_path, false);
|
|
if (prefixed_map_pluginsfile[0] != '\0')
|
|
{
|
|
g_plugins.loadPluginsFromFile(prefixed_map_pluginsfile, false);
|
|
}
|
|
|
|
g_plugins.Finalize();
|
|
g_plugins.InvalidateCache();
|
|
|
|
// Register forwards
|
|
FF_PluginInit = registerForward("plugin_init", ET_IGNORE, FP_DONE);
|
|
FF_ClientCommand = registerForward("client_command", ET_STOP, FP_CELL, FP_DONE);
|
|
FF_ClientConnect = registerForward("client_connect", ET_IGNORE, FP_CELL, FP_DONE);
|
|
FF_ClientDisconnect = registerForward("client_disconnect", ET_IGNORE, FP_CELL, FP_DONE);
|
|
FF_ClientDisconnected = registerForward("client_disconnected", ET_IGNORE, FP_CELL, FP_CELL, FP_ARRAY, FP_CELL, FP_DONE);
|
|
FF_ClientRemove = registerForward("client_remove", ET_IGNORE, FP_CELL, FP_CELL, FP_STRING, FP_DONE);
|
|
FF_ClientInfoChanged = registerForward("client_infochanged", ET_IGNORE, FP_CELL, FP_DONE);
|
|
FF_ClientPutInServer = registerForward("client_putinserver", ET_IGNORE, FP_CELL, FP_DONE);
|
|
FF_PluginCfg = registerForward("plugin_cfg", ET_IGNORE, FP_DONE);
|
|
FF_PluginPrecache = registerForward("plugin_precache", ET_IGNORE, FP_DONE);
|
|
FF_PluginLog = registerForward("plugin_log", ET_STOP, FP_DONE);
|
|
FF_PluginEnd = registerForward("plugin_end", ET_IGNORE, FP_DONE);
|
|
FF_InconsistentFile = registerForward("inconsistent_file", ET_STOP, FP_CELL, FP_STRING, FP_STRINGEX, FP_DONE);
|
|
FF_ClientAuthorized = registerForward("client_authorized", ET_IGNORE, FP_CELL, FP_STRING, FP_DONE);
|
|
FF_ChangeLevel = registerForward("server_changelevel", ET_STOP, FP_STRING, FP_DONE);
|
|
FF_ClientConnectEx = registerForward("client_connectex", ET_STOP, FP_CELL, FP_STRING, FP_STRING, FP_ARRAY, FP_DONE);
|
|
|
|
CoreCfg.OnAmxxInitialized();
|
|
|
|
#if defined BINLOG_ENABLED
|
|
if (!g_BinLog.Open())
|
|
{
|
|
LOG_ERROR(PLID, "Binary log failed to open.");
|
|
}
|
|
g_binlog_level = atoi(get_localinfo("bin_logging", "17"));
|
|
g_binlog_maxsize = atoi(get_localinfo("max_binlog_size", "20"));
|
|
#endif
|
|
|
|
modules_callPluginsLoaded();
|
|
|
|
TypeConversion.init();
|
|
|
|
// ###### Call precache forward function
|
|
g_dontprecache = false;
|
|
executeForwards(FF_PluginPrecache);
|
|
g_dontprecache = true;
|
|
|
|
for (auto &generic : g_forcegeneric)
|
|
{
|
|
PRECACHE_GENERIC(generic->getFilename());
|
|
ENGINE_FORCE_UNMODIFIED(generic->getForceType(), generic->getMin(), generic->getMax(), generic->getFilename());
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
struct sUserMsg
|
|
{
|
|
const char* name;
|
|
int* id;
|
|
funEventCall func;
|
|
bool endmsg;
|
|
bool cstrike;
|
|
} g_user_msg[] =
|
|
{
|
|
{"CurWeapon", &gmsgCurWeapon, Client_CurWeapon, false, false},
|
|
{"Damage", &gmsgDamage, Client_DamageEnd, true, true},
|
|
{"DeathMsg", &gmsgDeathMsg, Client_DeathMsg, false, true},
|
|
{"TextMsg", &gmsgTextMsg, Client_TextMsg, false, false},
|
|
{"TeamInfo", &gmsgTeamInfo, Client_TeamInfo, false, false},
|
|
{"WeaponList", &gmsgWeaponList, Client_WeaponList, false, false},
|
|
{"MOTD", &gmsgMOTD, 0, false, false},
|
|
{"ServerName", &gmsgServerName, 0, false, false},
|
|
{"Health", &gmsgHealth, 0, false, false},
|
|
{"Battery", &gmsgBattery, 0, false, false},
|
|
{"ShowMenu", &gmsgShowMenu, Client_ShowMenu, false, false},
|
|
{"SendAudio", &gmsgSendAudio, 0, false, false},
|
|
{"AmmoX", &gmsgAmmoX, Client_AmmoX, false, false},
|
|
{"ScoreInfo", &gmsgScoreInfo, Client_ScoreInfo, false, false},
|
|
{"VGUIMenu", &gmsgVGUIMenu, Client_VGUIMenu, false, false},
|
|
{"AmmoPickup", &gmsgAmmoPickup, Client_AmmoPickup, false, false},
|
|
{"WeapPickup", &gmsgWeapPickup, 0, false, false},
|
|
{"ResetHUD", &gmsgResetHUD, 0, false, false},
|
|
{"RoundTime", &gmsgRoundTime, 0, false, false},
|
|
{"SayText", &gmsgSayText, 0, false, false},
|
|
{"InitHUD", &gmsgInitHUD, Client_InitHUDEnd, true, false},
|
|
{0, 0, 0, false, false}
|
|
};
|
|
|
|
int C_RegUserMsg_Post(const char *pszName, int iSize)
|
|
{
|
|
for (int i = 0; g_user_msg[i].name; ++i)
|
|
{
|
|
if (strcmp(g_user_msg[i].name, pszName) == 0)
|
|
{
|
|
int id = META_RESULT_ORIG_RET(int);
|
|
*g_user_msg[i].id = id;
|
|
|
|
if (!g_user_msg[i].cstrike || g_bmod_cstrike)
|
|
{
|
|
if (g_user_msg[i].endmsg)
|
|
modMsgsEnd[id] = g_user_msg[i].func;
|
|
else
|
|
modMsgs[id] = g_user_msg[i].func;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
/*
|
|
Much more later after precache. All is precached, server
|
|
will be flaged as ready to use so call
|
|
plugin_init forward function from plugins
|
|
*/
|
|
void C_ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
|
|
{
|
|
int id;
|
|
|
|
for (int i = 0; g_user_msg[i].name; ++i)
|
|
{
|
|
if ((*g_user_msg[i].id == 0) && (id = GET_USER_MSG_ID(PLID, g_user_msg[i].name, NULL)) != 0)
|
|
{
|
|
*g_user_msg[i].id = id;
|
|
|
|
if (!g_user_msg[i].cstrike || g_bmod_cstrike)
|
|
{
|
|
if (g_user_msg[i].endmsg)
|
|
modMsgsEnd[id] = g_user_msg[i].func;
|
|
else
|
|
modMsgs[id] = g_user_msg[i].func;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_isDropClientHookAvailable)
|
|
{
|
|
if (!g_isDropClientHookEnabled)
|
|
{
|
|
if (RehldsApi)
|
|
{
|
|
RehldsHookchains->SV_DropClient()->registerHook(SV_DropClient_RH);
|
|
}
|
|
else
|
|
{
|
|
DropClientDetour->EnableDetour();
|
|
}
|
|
g_isDropClientHookEnabled = true;
|
|
}
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_ServerActivate_Post(edict_t *pEdictList, int edictCount, int clientMax)
|
|
{
|
|
if (g_activated)
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
for (int i = 1; i <= gpGlobals->maxClients; ++i)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER_I(i);
|
|
pPlayer->Init(pEdictList + i, i);
|
|
}
|
|
|
|
executeForwards(FF_PluginInit);
|
|
executeForwards(FF_PluginCfg);
|
|
|
|
CoreCfg.ExecuteMainConfig(); // Execute amxx.cfg
|
|
CoreCfg.ExecuteAutoConfigs(); // Execute configs created with AutoExecConfig native.
|
|
CoreCfg.SetMapConfigTimer(6.1); // Prepare per-map configs to be executed 6.1 seconds later.
|
|
// Original value which was used in admin.sma.
|
|
|
|
// Correct time in Counter-Strike and other mods (except DOD)
|
|
if (!g_bmod_dod)
|
|
g_game_timeleft = 0;
|
|
|
|
g_task_time = gpGlobals->time;
|
|
g_auth_time = gpGlobals->time;
|
|
|
|
#ifdef MEMORY_TEST
|
|
g_next_memreport_time = gpGlobals->time + MEMREPORT_INTERVAL;
|
|
g_memreport_count = 0;
|
|
g_memreport_enabled = true;
|
|
#endif
|
|
|
|
g_activated = true;
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
// Call plugin_end forward function from plugins.
|
|
void C_ServerDeactivate()
|
|
{
|
|
if (!g_activated)
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
for (int i = 1; i <= gpGlobals->maxClients; ++i)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER_I(i);
|
|
|
|
if (pPlayer->initialized)
|
|
{
|
|
// deprecated
|
|
executeForwards(FF_ClientDisconnect, static_cast<cell>(pPlayer->index));
|
|
|
|
if (g_isDropClientHookAvailable && !pPlayer->disconnecting)
|
|
{
|
|
executeForwards(FF_ClientDisconnected, static_cast<cell>(pPlayer->index), FALSE, prepareCharArray(const_cast<char*>(""), 0), 0);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->ingame)
|
|
{
|
|
auto wasDisconnecting = pPlayer->disconnecting;
|
|
|
|
pPlayer->Disconnect();
|
|
--g_players_num;
|
|
|
|
if (!wasDisconnecting && g_isDropClientHookAvailable)
|
|
{
|
|
executeForwards(FF_ClientRemove, static_cast<cell>(pPlayer->index), FALSE, const_cast<char*>(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_isDropClientHookAvailable)
|
|
{
|
|
if (g_isDropClientHookEnabled)
|
|
{
|
|
if (RehldsApi)
|
|
{
|
|
RehldsHookchains->SV_DropClient()->unregisterHook(SV_DropClient_RH);
|
|
}
|
|
else
|
|
{
|
|
DropClientDetour->DisableDetour();
|
|
}
|
|
g_isDropClientHookEnabled = false;
|
|
}
|
|
}
|
|
|
|
g_players_num = 0;
|
|
executeForwards(FF_PluginEnd);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
extern ke::Vector<cell *> g_hudsync;
|
|
|
|
// After all clear whole AMX configuration
|
|
// However leave AMX modules which are loaded only once
|
|
void C_ServerDeactivate_Post()
|
|
{
|
|
if (!g_initialized)
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
modules_callPluginsUnloading();
|
|
|
|
CoreCfg.Clear();
|
|
|
|
g_auth.clear();
|
|
g_commands.clear();
|
|
g_forcemodels.clear();
|
|
g_forcesounds.clear();
|
|
g_forcegeneric.clear();
|
|
g_grenades.clear();
|
|
g_tasksMngr.clear();
|
|
g_forwards.clear();
|
|
g_logevents.clearLogEvents();
|
|
g_events.clearEvents();
|
|
g_menucmds.clear();
|
|
ClearMenus();
|
|
g_vault.clear();
|
|
g_xvars.clear();
|
|
g_plugins.clear();
|
|
|
|
g_CvarManager.OnPluginUnloaded();
|
|
|
|
ClearPluginLibraries();
|
|
modules_callPluginsUnloaded();
|
|
|
|
detachReloadModules();
|
|
|
|
ClearMessages();
|
|
|
|
// Flush the dynamic admins list
|
|
for (size_t iter=DynamicAdmins.length();iter--; )
|
|
{
|
|
delete DynamicAdmins[iter];
|
|
}
|
|
|
|
DynamicAdmins.clear();
|
|
for (unsigned int i=0; i<g_hudsync.length(); i++)
|
|
delete [] g_hudsync[i];
|
|
g_hudsync.clear();
|
|
|
|
FlagMan.WriteCommands();
|
|
|
|
// last memreport
|
|
#ifdef MEMORY_TEST
|
|
if (g_memreport_enabled)
|
|
{
|
|
if (g_memreport_count == 0)
|
|
{
|
|
// make new directory
|
|
time_t td;
|
|
time(&td);
|
|
tm *curTime = localtime(&td);
|
|
int i = 0;
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
mkdir(build_pathname("%s/memreports", get_localinfo("amxx_basedir", "addons/amxmodx")), 0700);
|
|
#else
|
|
mkdir(build_pathname("%s/memreports", get_localinfo("amxx_basedir", "addons/amxmodx")));
|
|
#endif
|
|
while (true)
|
|
{
|
|
char buffer[256];
|
|
sprintf(buffer, "%s/memreports/D%02d%02d%03d", get_localinfo("amxx_basedir", "addons/amxmodx"), curTime->tm_mon + 1, curTime->tm_mday, i);
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
mkdir(build_pathname("%s", g_log_dir.chars()), 0700);
|
|
if (mkdir(build_pathname(buffer), 0700) < 0)
|
|
#else
|
|
mkdir(build_pathname("%s", g_log_dir.chars()));
|
|
if (mkdir(build_pathname(buffer)) < 0)
|
|
#endif
|
|
{
|
|
if (errno == EEXIST)
|
|
{
|
|
// good
|
|
++i;
|
|
continue;
|
|
} else {
|
|
// bad
|
|
g_memreport_enabled = false;
|
|
AMXXLOG_Log("[AMXX] Fatal error: Can't create directory for memreport files (%s)", buffer);
|
|
break;
|
|
}
|
|
}
|
|
g_memreport_dir = buffer;
|
|
|
|
// g_memreport_dir should be valid now
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_dumpMemoryReport(build_pathname("%s/r%03d.txt", g_memreport_dir.chars(), g_memreport_count));
|
|
AMXXLOG_Log("Memreport #%d created (file \"%s/r%03d.txt\") (interval %f)", g_memreport_count + 1, g_memreport_dir.chars(), g_memreport_count, MEMREPORT_INTERVAL);
|
|
|
|
g_memreport_count++;
|
|
}
|
|
#endif // MEMORY_TEST
|
|
|
|
#if defined BINLOG_ENABLED
|
|
g_BinLog.Close();
|
|
#endif
|
|
|
|
g_initialized = false;
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
BOOL C_ClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128])
|
|
{
|
|
CPlayer* pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
if (!pPlayer->IsBot())
|
|
{
|
|
bool a = pPlayer->Connect(pszName, pszAddress);
|
|
executeForwards(FF_ClientConnect, static_cast<cell>(pPlayer->index));
|
|
|
|
if (a)
|
|
{
|
|
auto playerToAuth = ke::AutoPtr<CPlayer *>(new CPlayer*(pPlayer));
|
|
if (playerToAuth)
|
|
g_auth.append(ke::Move(playerToAuth));
|
|
} else {
|
|
pPlayer->Authorize();
|
|
const char* authid = GETPLAYERAUTHID(pEntity);
|
|
if (g_auth_funcs.size())
|
|
{
|
|
List<AUTHORIZEFUNC>::iterator iter, end=g_auth_funcs.end();
|
|
AUTHORIZEFUNC fn;
|
|
for (iter=g_auth_funcs.begin(); iter!=end; iter++)
|
|
{
|
|
fn = (*iter);
|
|
fn(pPlayer->index, authid);
|
|
}
|
|
}
|
|
executeForwards(FF_ClientAuthorized, static_cast<cell>(pPlayer->index), authid);
|
|
}
|
|
}
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, TRUE);
|
|
}
|
|
|
|
BOOL C_ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128])
|
|
{
|
|
CPlayer* pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
|
|
if(executeForwards(FF_ClientConnectEx, static_cast<cell>(pPlayer->index), pszName, pszAddress, prepareCharArray(szRejectReason, 128, true)))
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, FALSE);
|
|
|
|
RETURN_META_VALUE(MRES_IGNORED, TRUE);
|
|
}
|
|
|
|
void C_ClientDisconnect(edict_t *pEntity)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
|
|
if (pPlayer->initialized)
|
|
{
|
|
// deprecated
|
|
executeForwards(FF_ClientDisconnect, static_cast<cell>(pPlayer->index));
|
|
|
|
if (g_isDropClientHookAvailable && !pPlayer->disconnecting)
|
|
{
|
|
executeForwards(FF_ClientDisconnected, static_cast<cell>(pPlayer->index), FALSE, prepareCharArray(const_cast<char*>(""), 0), 0);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->ingame)
|
|
{
|
|
--g_players_num;
|
|
}
|
|
|
|
auto wasDisconnecting = pPlayer->disconnecting;
|
|
|
|
pPlayer->Disconnect();
|
|
|
|
if (!wasDisconnecting && g_isDropClientHookAvailable)
|
|
{
|
|
executeForwards(FF_ClientRemove, static_cast<cell>(pPlayer->index), FALSE, const_cast<char*>(""));
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
CPlayer* SV_DropClient_PreHook(edict_s *client, qboolean crash, const char *buffer, size_t buffer_size)
|
|
{
|
|
auto pPlayer = client ? GET_PLAYER_POINTER(client) : nullptr;
|
|
|
|
if (pPlayer)
|
|
{
|
|
if (pPlayer->initialized)
|
|
{
|
|
pPlayer->disconnecting = true;
|
|
executeForwards(FF_ClientDisconnected, pPlayer->index, TRUE, prepareCharArray(const_cast<char*>(buffer), buffer_size, true), buffer_size - 1);
|
|
}
|
|
}
|
|
|
|
return pPlayer;
|
|
}
|
|
|
|
void SV_DropClient_PostHook(CPlayer *pPlayer, qboolean crash, const char *buffer)
|
|
{
|
|
if (pPlayer)
|
|
{
|
|
pPlayer->Disconnect();
|
|
executeForwards(FF_ClientRemove, pPlayer->index, TRUE, buffer);
|
|
}
|
|
}
|
|
|
|
// void SV_DropClient(client_t *cl, qboolean crash, const char *fmt, ...);
|
|
DETOUR_DECL_STATIC3_VAR(SV_DropClient, void, client_t*, cl, qboolean, crash, const char*, format)
|
|
{
|
|
char buffer[1024];
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
ke::SafeVsprintf(buffer, sizeof(buffer) - 1, format, ap);
|
|
va_end(ap);
|
|
|
|
auto pPlayer = SV_DropClient_PreHook(cl->edict, crash, buffer, ARRAY_LENGTH(buffer));
|
|
|
|
DETOUR_STATIC_CALL(SV_DropClient)(cl, crash, "%s", buffer);
|
|
|
|
SV_DropClient_PostHook(pPlayer, crash, buffer);
|
|
}
|
|
|
|
void SV_DropClient_RH(IRehldsHook_SV_DropClient *chain, IGameClient *cl, bool crash, const char *format)
|
|
{
|
|
char buffer[1024];
|
|
ke::SafeStrcpy(buffer, sizeof(buffer), format);
|
|
|
|
auto pPlayer = SV_DropClient_PreHook(cl->GetEdict(), crash, buffer, ARRAY_LENGTH(buffer));
|
|
|
|
chain->callNext(cl, crash, buffer);
|
|
|
|
SV_DropClient_PostHook(pPlayer, crash, buffer);
|
|
}
|
|
|
|
void C_ClientPutInServer_Post(edict_t *pEntity)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
if (!pPlayer->IsBot())
|
|
{
|
|
pPlayer->PutInServer();
|
|
++g_players_num;
|
|
executeForwards(FF_ClientPutInServer, static_cast<cell>(pPlayer->index));
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_ClientUserInfoChanged_Post(edict_t *pEntity, char *infobuffer)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
executeForwards(FF_ClientInfoChanged, static_cast<cell>(pPlayer->index));
|
|
const char* name = INFOKEY_VALUE(infobuffer, "name");
|
|
|
|
// Emulate bot connection and putinserver
|
|
if (pPlayer->ingame)
|
|
{
|
|
pPlayer->name =name; // Make sure player have name up to date
|
|
} else if (pPlayer->IsBot()) {
|
|
pPlayer->Connect(name, "127.0.0.1"/*CVAR_GET_STRING("net_address")*/);
|
|
|
|
executeForwards(FF_ClientConnect, static_cast<cell>(pPlayer->index));
|
|
|
|
pPlayer->Authorize();
|
|
const char* authid = GETPLAYERAUTHID(pEntity);
|
|
if (g_auth_funcs.size())
|
|
{
|
|
List<AUTHORIZEFUNC>::iterator iter, end=g_auth_funcs.end();
|
|
AUTHORIZEFUNC fn;
|
|
for (iter=g_auth_funcs.begin(); iter!=end; iter++)
|
|
{
|
|
fn = (*iter);
|
|
fn(pPlayer->index, authid);
|
|
}
|
|
}
|
|
executeForwards(FF_ClientAuthorized, static_cast<cell>(pPlayer->index), authid);
|
|
|
|
pPlayer->PutInServer();
|
|
++g_players_num;
|
|
|
|
executeForwards(FF_ClientPutInServer, static_cast<cell>(pPlayer->index));
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_ClientCommand(edict_t *pEntity)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER(pEntity);
|
|
|
|
META_RES result = MRES_IGNORED;
|
|
cell ret = 0;
|
|
|
|
const char* cmd = CMD_ARGV(0);
|
|
const char* arg = CMD_ARGV(1);
|
|
|
|
// Handle "amxx" if not on listenserver
|
|
if (IS_DEDICATED_SERVER())
|
|
{
|
|
if (cmd && stricmp(cmd, "amxx") == 0)
|
|
{
|
|
// Print version
|
|
static char buf[1024];
|
|
size_t len = 0;
|
|
|
|
sprintf(buf, "%s %s\n", Plugin_info.name, Plugin_info.version);
|
|
CLIENT_PRINT(pEntity, print_console, buf);
|
|
len = sprintf(buf, "Authors: \n David \"BAILOPAN\" Anderson, Pavol \"PM OnoTo\" Marko, Felix \"SniperBeamer\" Geyer\n");
|
|
len += sprintf(&buf[len], " Jonny \"Got His Gun\" Bergstrom, Lukasz \"SidLuke\" Wlasinski\n");
|
|
CLIENT_PRINT(pEntity, print_console, buf);
|
|
len = sprintf(buf, " Christian \"Basic-Master\" Hammacher, Borja \"faluco\" Ferrer\n");
|
|
len += sprintf(&buf[len], " Scott \"DS\" Ehlert\n");
|
|
len += sprintf(&buf[len], "Compiled: %s\nURL:http://www.amxmodx.org/\n", __DATE__ ", " __TIME__);
|
|
CLIENT_PRINT(pEntity, print_console, buf);
|
|
#ifdef JIT
|
|
sprintf(buf, "Core mode: JIT\n");
|
|
#else
|
|
#ifdef ASM32
|
|
sprintf(buf, "Core mode: ASM\n");
|
|
#else
|
|
sprintf(buf, "Core mode: Normal\n");
|
|
#endif
|
|
#endif
|
|
CLIENT_PRINT(pEntity, print_console, buf);
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
}
|
|
|
|
if (executeForwards(FF_ClientCommand, static_cast<cell>(pPlayer->index)) > 0)
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
|
|
/* check for command and if needed also for first argument and call proper function */
|
|
|
|
CmdMngr::iterator aa = g_commands.clcmdprefixbegin(cmd);
|
|
|
|
if (!aa)
|
|
aa = g_commands.clcmdbegin();
|
|
|
|
while (aa)
|
|
{
|
|
if ((*aa).matchCommandLine(cmd, arg) && (*aa).getPlugin()->isExecutable((*aa).getFunction()))
|
|
{
|
|
ret = executeForwards((*aa).getFunction(), static_cast<cell>(pPlayer->index),
|
|
static_cast<cell>((*aa).getFlags()), static_cast<cell>((*aa).getId()));
|
|
if (ret & 2) result = MRES_SUPERCEDE;
|
|
if (ret & 1) RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
++aa;
|
|
}
|
|
|
|
/* check menu commands */
|
|
|
|
if (!strcmp(cmd, "menuselect"))
|
|
{
|
|
int pressed_key = atoi(arg) - 1;
|
|
int bit_key = (1<<pressed_key);
|
|
|
|
if (pPlayer->keys & bit_key)
|
|
{
|
|
if (gpGlobals->time > pPlayer->menuexpire)
|
|
{
|
|
if (Menu *pMenu = get_menu_by_id(pPlayer->newmenu))
|
|
{
|
|
pMenu->Close(pPlayer->index);
|
|
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
else if (pPlayer->menu > 0 && !pPlayer->vgui)
|
|
{
|
|
pPlayer->menu = 0;
|
|
pPlayer->keys = 0;
|
|
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
}
|
|
|
|
int menuid = pPlayer->menu;
|
|
pPlayer->menu = 0;
|
|
|
|
/* First, do new menus */
|
|
int func_was_executed = -1;
|
|
if (pPlayer->newmenu != -1)
|
|
{
|
|
int menu = pPlayer->newmenu;
|
|
pPlayer->newmenu = -1;
|
|
if (Menu *pMenu = get_menu_by_id(menu))
|
|
{
|
|
int item = pMenu->PagekeyToItem(pPlayer->page, pressed_key+1);
|
|
if (item == MENU_BACK)
|
|
{
|
|
if (pMenu->pageCallback >= 0)
|
|
executeForwards(pMenu->pageCallback, static_cast<cell>(pPlayer->index), static_cast<cell>(MENU_BACK));
|
|
|
|
pMenu->Display(pPlayer->index, pPlayer->page - 1);
|
|
} else if (item == MENU_MORE) {
|
|
if (pMenu->pageCallback >= 0)
|
|
executeForwards(pMenu->pageCallback, static_cast<cell>(pPlayer->index), static_cast<cell>(MENU_MORE));
|
|
|
|
pMenu->Display(pPlayer->index, pPlayer->page + 1);
|
|
} else {
|
|
ret = executeForwards(pMenu->func, static_cast<cell>(pPlayer->index), static_cast<cell>(menu), static_cast<cell>(item));
|
|
if (ret & 2)
|
|
{
|
|
result = MRES_SUPERCEDE;
|
|
} else if (ret & 1) {
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
}
|
|
/**
|
|
* No matter what we marked it as executed, since the callback styles are
|
|
* entirely different. After all, this is a backwards compat shim.
|
|
*/
|
|
func_was_executed = pMenu->func;
|
|
}
|
|
}
|
|
|
|
/* Now, do old menus */
|
|
MenuMngr::iterator a = g_menucmds.begin();
|
|
|
|
while (a)
|
|
{
|
|
g_menucmds.SetWatchIter(a);
|
|
if ((*a).matchCommand(menuid, bit_key)
|
|
&& (*a).getPlugin()->isExecutable((*a).getFunction())
|
|
&& (func_was_executed == -1
|
|
|| !g_forwards.isSameSPForward(func_was_executed, (*a).getFunction()))
|
|
)
|
|
{
|
|
ret = executeForwards((*a).getFunction(), static_cast<cell>(pPlayer->index),
|
|
static_cast<cell>(pressed_key), 0);
|
|
|
|
if (ret & 2) result = MRES_SUPERCEDE;
|
|
if (ret & 1) RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
if (g_menucmds.GetWatchIter() != a)
|
|
{
|
|
a = g_menucmds.GetWatchIter();
|
|
} else {
|
|
++a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for PLUGIN_HANDLED_MAIN and block hl call if needed */
|
|
RETURN_META(result);
|
|
}
|
|
|
|
void C_StartFrame_Post(void)
|
|
{
|
|
if (g_auth_time < gpGlobals->time)
|
|
{
|
|
g_auth_time = gpGlobals->time + 0.7f;
|
|
|
|
size_t i = 0;
|
|
while (i < g_auth.length())
|
|
{
|
|
auto player = g_auth[i].get();
|
|
const char* auth = GETPLAYERAUTHID((*player)->pEdict);
|
|
|
|
if ((auth == 0) || (*auth == 0))
|
|
{
|
|
g_auth.remove(i);
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(auth, "STEAM_ID_PENDING"))
|
|
{
|
|
(*player)->Authorize();
|
|
if (g_auth_funcs.size())
|
|
{
|
|
List<AUTHORIZEFUNC>::iterator iter, end=g_auth_funcs.end();
|
|
AUTHORIZEFUNC fn;
|
|
for (iter=g_auth_funcs.begin(); iter!=end; iter++)
|
|
{
|
|
fn = (*iter);
|
|
fn((*player)->index, auth);
|
|
}
|
|
}
|
|
executeForwards(FF_ClientAuthorized, static_cast<cell>((*player)->index), auth);
|
|
g_auth.remove(i);
|
|
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
#ifdef MEMORY_TEST
|
|
if (g_memreport_enabled && g_next_memreport_time <= gpGlobals->time)
|
|
{
|
|
g_next_memreport_time = gpGlobals->time + MEMREPORT_INTERVAL;
|
|
|
|
if (g_memreport_count == 0)
|
|
{
|
|
// make new directory
|
|
time_t td;
|
|
time(&td);
|
|
tm *curTime = localtime(&td);
|
|
|
|
int i = 0;
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
mkdir(build_pathname("%s/memreports", get_localinfo("amxx_basedir", "addons/amxmodx")), 0700);
|
|
#else
|
|
mkdir(build_pathname("%s/memreports", get_localinfo("amxx_basedir", "addons/amxmodx")));
|
|
#endif
|
|
while (true)
|
|
{
|
|
char buffer[256];
|
|
sprintf(buffer, "%s/memreports/D%02d%02d%03d", get_localinfo("amxx_basedir", "addons/amxmodx"), curTime->tm_mon + 1, curTime->tm_mday, i);
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
mkdir(build_pathname("%s", g_log_dir.chars()), 0700);
|
|
if (mkdir(build_pathname(buffer), 0700) < 0)
|
|
#else
|
|
mkdir(build_pathname("%s", g_log_dir.chars()));
|
|
if (mkdir(build_pathname(buffer)) < 0)
|
|
#endif
|
|
{
|
|
if (errno == EEXIST)
|
|
{
|
|
// good
|
|
++i;
|
|
continue;
|
|
} else {
|
|
// bad
|
|
g_memreport_enabled = false;
|
|
AMXXLOG_Log("[AMXX] Fatal error: Can't create directory for memreport files (%s)", buffer);
|
|
break;
|
|
}
|
|
}
|
|
g_memreport_dir = buffer;
|
|
// g_memreport_dir should be valid now
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_dumpMemoryReport(build_pathname("%s/r%03d.txt", g_memreport_dir.chars(), g_memreport_count));
|
|
AMXXLOG_Log("Memreport #%d created (file \"%s/r%03d.txt\") (interval %f)", g_memreport_count + 1, g_memreport_dir.chars(), g_memreport_count, MEMREPORT_INTERVAL);
|
|
|
|
g_memreport_count++;
|
|
}
|
|
#endif // MEMORY_TEST
|
|
|
|
g_frameActionMngr.ExecuteFrameCallbacks();
|
|
|
|
if (g_task_time > gpGlobals->time)
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
g_task_time = gpGlobals->time + 0.1f;
|
|
g_tasksMngr.startFrame();
|
|
|
|
CoreCfg.OnMapConfigTimer();
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_MessageBegin_Post(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed)
|
|
{
|
|
if (ed)
|
|
{
|
|
mPlayerIndex = ENTINDEX(ed);
|
|
mPlayer = GET_PLAYER_POINTER_I(mPlayerIndex);
|
|
} else {
|
|
mPlayerIndex = 0;
|
|
mPlayer = 0;
|
|
}
|
|
|
|
if (msg_type < 0 || msg_type >= MAX_REG_MSGS)
|
|
msg_type = 0;
|
|
|
|
mState = 0;
|
|
function = modMsgs[msg_type];
|
|
endfunction = modMsgsEnd[msg_type];
|
|
|
|
g_events.parserInit(msg_type, &gpGlobals->time, mPlayer, mPlayerIndex);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteByte_Post(int iValue)
|
|
{
|
|
g_events.parseValue(iValue);
|
|
if (function) (*function)((void *)&iValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteChar_Post(int iValue)
|
|
{
|
|
g_events.parseValue(iValue);
|
|
if (function) (*function)((void *)&iValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteShort_Post(int iValue)
|
|
{
|
|
g_events.parseValue(iValue);
|
|
if (function) (*function)((void *)&iValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteLong_Post(int iValue)
|
|
{
|
|
g_events.parseValue(iValue);
|
|
if (function) (*function)((void *)&iValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteAngle_Post(float flValue)
|
|
{
|
|
g_events.parseValue(flValue);
|
|
if (function) (*function)((void *)&flValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteCoord_Post(float flValue)
|
|
{
|
|
g_events.parseValue(flValue);
|
|
if (function) (*function)((void *)&flValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteString_Post(const char *sz)
|
|
{
|
|
g_events.parseValue(sz);
|
|
if (function) (*function)((void *)sz);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_WriteEntity_Post(int iValue)
|
|
{
|
|
g_events.parseValue(iValue);
|
|
if (function) (*function)((void *)&iValue);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_MessageEnd_Post(void)
|
|
{
|
|
g_events.executeEvents();
|
|
if (endfunction) (*endfunction)(NULL);
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
const char *C_Cmd_Args(void)
|
|
{
|
|
// if the global "fake" flag is set, which means that engclient_cmd was used, supercede the function
|
|
if (g_fakecmd.fake)
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, (g_fakecmd.argc > 1) ? g_fakecmd.args : g_fakecmd.argv[0]);
|
|
|
|
// otherwise ignore it
|
|
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
|
}
|
|
|
|
const char *C_Cmd_Argv(int argc)
|
|
{
|
|
// if the global "fake" flag is set, which means that engclient_cmd was used, supercede the function
|
|
if (g_fakecmd.fake)
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, (argc < 3) ? g_fakecmd.argv[argc] : "");
|
|
|
|
// otherwise ignore it
|
|
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
|
}
|
|
|
|
int C_Cmd_Argc(void)
|
|
{
|
|
// if the global "fake" flag is set, which means that engclient_cmd was used, supercede the function
|
|
if (g_fakecmd.fake)
|
|
RETURN_META_VALUE(MRES_SUPERCEDE, g_fakecmd.argc);
|
|
|
|
// otherwise ignore it
|
|
RETURN_META_VALUE(MRES_IGNORED, 0);
|
|
}
|
|
|
|
// Grenade has been thrown.
|
|
// Only here we may find out who is an owner.
|
|
void C_SetModel(edict_t *e, const char *m)
|
|
{
|
|
if (e->v.owner && m[7]=='w' && m[8]=='_' && m[9]=='h')
|
|
g_grenades.put(e, 1.75, 4, GET_PLAYER_POINTER(e->v.owner));
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
// Save at what part of body a player is aiming
|
|
void C_TraceLine_Post(const float *v1, const float *v2, int fNoMonsters, edict_t *e, TraceResult *ptr)
|
|
{
|
|
if (e && (e->v.flags & (FL_CLIENT | FL_FAKECLIENT)))
|
|
{
|
|
CPlayer* pPlayer = GET_PLAYER_POINTER(e);
|
|
|
|
if (ptr->pHit && (ptr->pHit->v.flags & (FL_CLIENT | FL_FAKECLIENT)))
|
|
pPlayer->aiming = ptr->iHitgroup;
|
|
|
|
pPlayer->lastTrace = ptr->vecEndPos;
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_AlertMessage(ALERT_TYPE atype, const char *szFmt, ...)
|
|
{
|
|
if (atype != at_logged)
|
|
{
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
/* There are also more messages but we want only logs
|
|
at_notice,
|
|
at_console, // same as at_notice, but forces a ConPrintf, not a message box
|
|
at_aiconsole, // same as at_console, but only shown if developer level is 2!
|
|
at_warning,
|
|
at_error,
|
|
at_logged // Server print to console ( only in multiplayer games ).
|
|
*/
|
|
|
|
cell retVal = 0;
|
|
|
|
// execute logevents and plugin_log forward
|
|
if (g_logevents.logEventsExist()
|
|
|| g_forwards.getFuncsNum(FF_PluginLog))
|
|
{
|
|
va_list logArgPtr;
|
|
va_start(logArgPtr, szFmt);
|
|
g_logevents.setLogString(szFmt, logArgPtr);
|
|
va_end(logArgPtr);
|
|
g_logevents.parseLogString();
|
|
|
|
if (g_logevents.logEventsExist())
|
|
{
|
|
g_logevents.executeLogEvents();
|
|
}
|
|
|
|
retVal = executeForwards(FF_PluginLog);
|
|
}
|
|
|
|
if (retVal)
|
|
{
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
}
|
|
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_ChangeLevel(const char *map, const char *what)
|
|
{
|
|
int ret = executeForwards(FF_ChangeLevel, map);
|
|
if (ret)
|
|
RETURN_META(MRES_SUPERCEDE);
|
|
RETURN_META(MRES_IGNORED);
|
|
}
|
|
|
|
void C_CvarValue2(const edict_t *pEdict, int requestId, const char *cvar, const char *value)
|
|
{
|
|
CPlayer *pPlayer = GET_PLAYER_POINTER(pEdict);
|
|
if (pPlayer->queries.empty())
|
|
RETURN_META(MRES_IGNORED);
|
|
|
|
List<ClientCvarQuery_Info *>::iterator iter, end=pPlayer->queries.end();
|
|
ClientCvarQuery_Info *info;
|
|
for (iter=pPlayer->queries.begin(); iter!=end; iter++)
|
|
{
|
|
info = (*iter);
|
|
if ( info->requestId == requestId )
|
|
{
|
|
if (info->paramLen)
|
|
{
|
|
cell arr = prepareCellArray(info->params, info->paramLen);
|
|
executeForwards(info->resultFwd, static_cast<cell>(ENTINDEX(pEdict)),
|
|
cvar, value, arr);
|
|
} else {
|
|
executeForwards(info->resultFwd, static_cast<cell>(ENTINDEX(pEdict)),
|
|
cvar, value);
|
|
}
|
|
unregisterSPForward(info->resultFwd);
|
|
pPlayer->queries.erase(iter);
|
|
delete [] info->params;
|
|
delete info;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
RETURN_META(MRES_HANDLED);
|
|
}
|
|
|
|
C_DLLEXPORT int Meta_Query(const char *ifvers, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs)
|
|
{
|
|
gpMetaUtilFuncs = pMetaUtilFuncs;
|
|
*pPlugInfo = &Plugin_info;
|
|
|
|
int mmajor = 0, mminor = 0, pmajor = 0, pminor = 0;
|
|
|
|
sscanf(ifvers, "%d:%d", &mmajor, &mminor);
|
|
sscanf(Plugin_info.ifvers, "%d:%d", &pmajor, &pminor);
|
|
|
|
if (strcmp(ifvers, Plugin_info.ifvers))
|
|
{
|
|
LOG_MESSAGE(PLID, "warning: ifvers mismatch (pl \"%s\") (mm \"%s\")", Plugin_info.ifvers, ifvers);
|
|
if (pmajor > mmajor)
|
|
{
|
|
LOG_ERROR(PLID, "metamod version is too old for this plugin; update metamod");
|
|
return (FALSE);
|
|
} else if (pmajor < mmajor) {
|
|
LOG_ERROR(PLID, "metamod version is incompatible with this plugin; please find a newer version of this plugin");
|
|
return (FALSE);
|
|
} else if (pmajor == mmajor) {
|
|
if (pminor > mminor)
|
|
{
|
|
LOG_ERROR(PLID, "metamod version is incompatible with this plugin; please find a newer version of this plugin");
|
|
return FALSE;
|
|
} else if (pminor < mminor) {
|
|
LOG_MESSAGE(PLID, "warning: there may be a newer version of metamod available");
|
|
}
|
|
}
|
|
}
|
|
|
|
// :NOTE: Don't call modules query here (g_FakeMeta.Meta_Query), because we don't know modules yet. Do it in Meta_Attach
|
|
return (TRUE);
|
|
}
|
|
|
|
static META_FUNCTIONS gMetaFunctionTable;
|
|
C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs)
|
|
{
|
|
if (now > Plugin_info.loadable)
|
|
{
|
|
LOG_ERROR(PLID, "Can't load plugin right now");
|
|
return (FALSE);
|
|
}
|
|
|
|
gpMetaGlobals = pMGlobals;
|
|
gMetaFunctionTable.pfnGetEntityAPI2 = GetEntityAPI2;
|
|
gMetaFunctionTable.pfnGetEntityAPI2_Post = GetEntityAPI2_Post;
|
|
gMetaFunctionTable.pfnGetEngineFunctions = GetEngineFunctions;
|
|
gMetaFunctionTable.pfnGetEngineFunctions_Post = GetEngineFunctions_Post;
|
|
#if !defined AMD64
|
|
gMetaFunctionTable.pfnGetNewDLLFunctions = GetNewDLLFunctions;
|
|
#endif
|
|
|
|
memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS));
|
|
gpGamedllFuncs=pGamedllFuncs;
|
|
|
|
Module_CacheFunctions();
|
|
|
|
CVAR_REGISTER(&init_amxmodx_version);
|
|
CVAR_REGISTER(&init_amxmodx_modules);
|
|
CVAR_REGISTER(&init_amxmodx_debug);
|
|
CVAR_REGISTER(&init_amxmodx_mldebug);
|
|
CVAR_REGISTER(&init_amxmodx_language);
|
|
CVAR_REGISTER(&init_amxmodx_cl_langs);
|
|
|
|
amxmodx_version = CVAR_GET_POINTER(init_amxmodx_version.name);
|
|
amxmodx_language = CVAR_GET_POINTER(init_amxmodx_language.name);
|
|
|
|
REG_SVR_COMMAND("amxx", amx_command);
|
|
|
|
char gameDir[512];
|
|
GET_GAME_DIR(gameDir);
|
|
char *a = gameDir;
|
|
int i = 0;
|
|
|
|
while (gameDir[i])
|
|
if (gameDir[i++] == '/')
|
|
a = &gameDir[i];
|
|
|
|
g_mod_name = a;
|
|
|
|
g_coloredmenus = ColoredMenus(g_mod_name.chars()); // whether or not to use colored menus
|
|
|
|
// ###### Print short GPL
|
|
print_srvconsole("\n AMX Mod X version %s Copyright (c) 2004-2015 AMX Mod X Development Team \n"
|
|
" AMX Mod X comes with ABSOLUTELY NO WARRANTY; for details type `amxx gpl'.\n", AMXX_VERSION);
|
|
print_srvconsole(" This is free software and you are welcome to redistribute it under \n"
|
|
" certain conditions; type 'amxx gpl' for details.\n \n");
|
|
|
|
// ###### Load custom path configuration
|
|
Vault amx_config;
|
|
amx_config.setSource(build_pathname("%s", get_localinfo("amxx_cfg", "addons/amxmodx/configs/core.ini")));
|
|
|
|
if (amx_config.loadVault())
|
|
{
|
|
Vault::iterator a = amx_config.begin();
|
|
|
|
while (a != amx_config.end())
|
|
{
|
|
SET_LOCALINFO((char*)a.key().chars(), (char*)a.value().chars());
|
|
++a;
|
|
}
|
|
amx_config.clear();
|
|
}
|
|
|
|
// ###### Initialize logging here
|
|
g_log_dir = get_localinfo("amxx_logs", "addons/amxmodx/logs");
|
|
g_log.SetLogType("amxx_logging");
|
|
|
|
// ###### Now attach metamod modules
|
|
// This will also call modules Meta_Query and Meta_Attach functions
|
|
loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), now);
|
|
|
|
GET_HOOK_TABLES(PLID, &g_pEngTable, NULL, NULL);
|
|
|
|
FlagMan.SetFile("cmdaccess.ini");
|
|
|
|
ConfigManager.OnAmxxStartup();
|
|
|
|
if (RehldsApi_Init())
|
|
{
|
|
RehldsHookchains->SV_DropClient()->registerHook(SV_DropClient_RH);
|
|
g_isDropClientHookAvailable = true;
|
|
g_isDropClientHookEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
void *address = nullptr;
|
|
|
|
if (CommonConfig && CommonConfig->GetMemSig("SV_DropClient", &address) && address)
|
|
{
|
|
DropClientDetour = DETOUR_CREATE_STATIC_FIXED(SV_DropClient, address);
|
|
g_isDropClientHookAvailable = true;
|
|
g_isDropClientHookEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
auto reason = RehldsApi ? "update ReHLDS" : "check your gamedata files";
|
|
AMXXLOG_Log("client_disconnected and client_remove forwards have been disabled - %s.", reason);
|
|
}
|
|
}
|
|
|
|
g_CvarManager.CreateCvarHook();
|
|
|
|
GET_IFACE<IFileSystem>("filesystem_stdio", g_FileSystem, FILESYSTEM_INTERFACE_VERSION);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason)
|
|
{
|
|
if (now > Plugin_info.unloadable && reason != PNL_CMD_FORCED)
|
|
{
|
|
LOG_ERROR(PLID, "Can't unload plugin right now");
|
|
return (FALSE);
|
|
}
|
|
|
|
modules_callPluginsUnloading();
|
|
|
|
g_auth.clear();
|
|
g_forwards.clear();
|
|
g_commands.clear();
|
|
g_forcemodels.clear();
|
|
g_forcesounds.clear();
|
|
g_forcegeneric.clear();
|
|
g_grenades.clear();
|
|
g_tasksMngr.clear();
|
|
g_logevents.clearLogEvents();
|
|
g_events.clearEvents();
|
|
g_menucmds.clear();
|
|
ClearMenus();
|
|
g_vault.clear();
|
|
g_xvars.clear();
|
|
g_plugins.clear();
|
|
g_langMngr.Clear();
|
|
|
|
ClearMessages();
|
|
|
|
modules_callPluginsUnloaded();
|
|
|
|
detachModules();
|
|
|
|
g_log.CloseFile();
|
|
|
|
Module_UncacheFunctions();
|
|
|
|
ClearLibraries(LibSource_Plugin);
|
|
ClearLibraries(LibSource_Module);
|
|
|
|
if (g_isDropClientHookAvailable)
|
|
{
|
|
if (RehldsApi)
|
|
{
|
|
if (g_isDropClientHookEnabled)
|
|
{
|
|
RehldsHookchains->SV_DropClient()->unregisterHook(SV_DropClient_RH);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DropClientDetour->Destroy();
|
|
}
|
|
g_isDropClientHookAvailable = false;
|
|
g_isDropClientHookEnabled = false;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals)
|
|
{
|
|
memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t));
|
|
gpGlobals = pGlobals;
|
|
}
|
|
|
|
DLL_FUNCTIONS gFunctionTable;
|
|
C_DLLEXPORT int GetEntityAPI2(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion)
|
|
{
|
|
memset(&gFunctionTable, 0, sizeof(DLL_FUNCTIONS));
|
|
|
|
gFunctionTable.pfnSpawn = C_Spawn;
|
|
gFunctionTable.pfnClientCommand = C_ClientCommand;
|
|
gFunctionTable.pfnServerDeactivate = C_ServerDeactivate;
|
|
gFunctionTable.pfnClientDisconnect = C_ClientDisconnect;
|
|
gFunctionTable.pfnInconsistentFile = C_InconsistentFile;
|
|
gFunctionTable.pfnServerActivate = C_ServerActivate;
|
|
gFunctionTable.pfnClientConnect = C_ClientConnect;
|
|
|
|
memcpy(pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS));
|
|
|
|
return 1;
|
|
}
|
|
|
|
DLL_FUNCTIONS gFunctionTable_Post;
|
|
C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion)
|
|
{
|
|
memset(&gFunctionTable_Post, 0, sizeof(DLL_FUNCTIONS));
|
|
|
|
gFunctionTable_Post.pfnClientPutInServer = C_ClientPutInServer_Post;
|
|
gFunctionTable_Post.pfnClientUserInfoChanged = C_ClientUserInfoChanged_Post;
|
|
gFunctionTable_Post.pfnServerActivate = C_ServerActivate_Post;
|
|
gFunctionTable_Post.pfnClientConnect = C_ClientConnect_Post;
|
|
gFunctionTable_Post.pfnStartFrame = C_StartFrame_Post;
|
|
gFunctionTable_Post.pfnServerDeactivate = C_ServerDeactivate_Post;
|
|
|
|
memcpy(pFunctionTable, &gFunctionTable_Post, sizeof(DLL_FUNCTIONS));
|
|
|
|
return 1;
|
|
}
|
|
|
|
enginefuncs_t meta_engfuncs;
|
|
C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion)
|
|
{
|
|
memset(&meta_engfuncs, 0, sizeof(enginefuncs_t));
|
|
|
|
if (stricmp(g_mod_name.chars(), "cstrike") == 0 || stricmp(g_mod_name.chars(), "czero") == 0)
|
|
{
|
|
meta_engfuncs.pfnSetModel = C_SetModel;
|
|
g_bmod_cstrike = true;
|
|
} else {
|
|
g_bmod_cstrike = false;
|
|
g_bmod_dod = !stricmp(g_mod_name.chars(), "dod");
|
|
g_bmod_tfc = !stricmp(g_mod_name.chars(), "tfc");
|
|
}
|
|
|
|
meta_engfuncs.pfnCmd_Argc = C_Cmd_Argc;
|
|
meta_engfuncs.pfnCmd_Argv = C_Cmd_Argv;
|
|
meta_engfuncs.pfnCmd_Args = C_Cmd_Args;
|
|
meta_engfuncs.pfnPrecacheModel = C_PrecacheModel;
|
|
meta_engfuncs.pfnPrecacheSound = C_PrecacheSound;
|
|
meta_engfuncs.pfnChangeLevel = C_ChangeLevel;
|
|
|
|
/* message stuff from messages.h/cpp */
|
|
meta_engfuncs.pfnMessageBegin = C_MessageBegin;
|
|
meta_engfuncs.pfnMessageEnd = C_MessageEnd;
|
|
meta_engfuncs.pfnWriteAngle = C_WriteAngle;
|
|
meta_engfuncs.pfnWriteByte = C_WriteByte;
|
|
meta_engfuncs.pfnWriteChar = C_WriteChar;
|
|
meta_engfuncs.pfnWriteCoord = C_WriteCoord;
|
|
meta_engfuncs.pfnWriteEntity = C_WriteEntity;
|
|
meta_engfuncs.pfnWriteLong = C_WriteLong;
|
|
meta_engfuncs.pfnWriteShort = C_WriteShort;
|
|
meta_engfuncs.pfnWriteString = C_WriteString;
|
|
|
|
meta_engfuncs.pfnAlertMessage = C_AlertMessage;
|
|
|
|
memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t));
|
|
|
|
return 1;
|
|
}
|
|
|
|
enginefuncs_t meta_engfuncs_post;
|
|
C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion)
|
|
{
|
|
memset(&meta_engfuncs_post, 0, sizeof(enginefuncs_t));
|
|
|
|
meta_engfuncs_post.pfnTraceLine = C_TraceLine_Post;
|
|
meta_engfuncs_post.pfnMessageBegin = C_MessageBegin_Post;
|
|
meta_engfuncs_post.pfnMessageEnd = C_MessageEnd_Post;
|
|
meta_engfuncs_post.pfnWriteByte = C_WriteByte_Post;
|
|
meta_engfuncs_post.pfnWriteChar = C_WriteChar_Post;
|
|
meta_engfuncs_post.pfnWriteShort = C_WriteShort_Post;
|
|
meta_engfuncs_post.pfnWriteLong = C_WriteLong_Post;
|
|
meta_engfuncs_post.pfnWriteAngle = C_WriteAngle_Post;
|
|
meta_engfuncs_post.pfnWriteCoord = C_WriteCoord_Post;
|
|
meta_engfuncs_post.pfnWriteString = C_WriteString_Post;
|
|
meta_engfuncs_post.pfnWriteEntity = C_WriteEntity_Post;
|
|
meta_engfuncs_post.pfnRegUserMsg = C_RegUserMsg_Post;
|
|
|
|
memcpy(pengfuncsFromEngine, &meta_engfuncs_post, sizeof(enginefuncs_t));
|
|
|
|
return 1;
|
|
}
|
|
|
|
//quick hack - disable all newdll stuff for AMD64
|
|
// until VALVe gets their act together!
|
|
#if !defined AMD64
|
|
NEW_DLL_FUNCTIONS gNewDLLFunctionTable;
|
|
C_DLLEXPORT int GetNewDLLFunctions(NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion)
|
|
{
|
|
memset(&gNewDLLFunctionTable, 0, sizeof(NEW_DLL_FUNCTIONS));
|
|
|
|
// default metamod does not call this if the gamedll doesn't provide it
|
|
if (g_engfuncs.pfnQueryClientCvarValue2)
|
|
{
|
|
gNewDLLFunctionTable.pfnCvarValue2 = C_CvarValue2;
|
|
g_NewDLL_Available = true;
|
|
}
|
|
|
|
memcpy(pNewFunctionTable, &gNewDLLFunctionTable, sizeof(NEW_DLL_FUNCTIONS));
|
|
|
|
return 1;
|
|
}
|
|
#endif
|