// 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 #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 #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 #include #include "CoreConfig.h" #include #include 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 g_auth_funcs; extern ke::Vector DynamicAdmins; CLog g_log; CForwardMngr g_forwards; ke::Vector> g_auth; ke::Vector> g_forcemodels; ke::Vector> g_forcesounds; ke::Vector> 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 & 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 & 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 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 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(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); } CoreCfg.ExecuteMainConfig(); // Execute amxx.cfg executeForwards(FF_PluginInit); executeForwards(FF_PluginCfg); 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(pPlayer->index)); if (g_isDropClientHookAvailable && !pPlayer->disconnecting) { executeForwards(FF_ClientDisconnected, static_cast(pPlayer->index), FALSE, prepareCharArray(const_cast(""), 0), 0); } } if (pPlayer->ingame) { auto wasDisconnecting = pPlayer->disconnecting; pPlayer->Disconnect(); --g_players_num; if (!wasDisconnecting && g_isDropClientHookAvailable) { executeForwards(FF_ClientRemove, static_cast(pPlayer->index), FALSE, const_cast("")); } } } 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 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; itm_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(pPlayer->index)); if (a) { auto playerToAuth = ke::AutoPtr(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::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(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(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(pPlayer->index)); if (g_isDropClientHookAvailable && !pPlayer->disconnecting) { executeForwards(FF_ClientDisconnected, static_cast(pPlayer->index), FALSE, prepareCharArray(const_cast(""), 0), 0); } } if (pPlayer->ingame) { --g_players_num; } auto wasDisconnecting = pPlayer->disconnecting; pPlayer->Disconnect(); if (!wasDisconnecting && g_isDropClientHookAvailable) { executeForwards(FF_ClientRemove, static_cast(pPlayer->index), FALSE, const_cast("")); } 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(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(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(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(pPlayer->index)); pPlayer->Authorize(); const char* authid = GETPLAYERAUTHID(pEntity); if (g_auth_funcs.size()) { List::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(pPlayer->index), authid); pPlayer->PutInServer(); ++g_players_num; executeForwards(FF_ClientPutInServer, static_cast(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(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(pPlayer->index), static_cast((*aa).getFlags()), static_cast((*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<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(pPlayer->index), static_cast(MENU_BACK)); pMenu->Display(pPlayer->index, pPlayer->page - 1); } else if (item == MENU_MORE) { if (pMenu->pageCallback >= 0) executeForwards(pMenu->pageCallback, static_cast(pPlayer->index), static_cast(MENU_MORE)); pMenu->Display(pPlayer->index, pPlayer->page + 1); } else { ret = executeForwards(pMenu->func, static_cast(pPlayer->index), static_cast(menu), static_cast(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(pPlayer->index), static_cast(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::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((*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 (!m || strcmp(m, "models/w_hegrenade.mdl") != 0) { RETURN_META(MRES_IGNORED); } if (e->v.owner) { g_grenades.put(e, 1.75f, 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::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(ENTINDEX(pEdict)), cvar, value, arr); } else { executeForwards(info->resultFwd, static_cast(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("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