amxmodx/amxmodx/meta_api.cpp
IgnacioFDM 65df386d66 Fix OnPluginsUnloaded not being called for reloadable modules. (#485)
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.
2018-03-09 19:32:30 +01:00

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