/* AMX Mod X * * by the AMX Mod X Development Team * originally developed by OLO * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. */ #include #if defined WIN32 #include #else #include #endif #include "amxmodx.h" #include "fakemeta.h" #include "newmenus.h" #include "natives.h" #include "binlog.h" #include "optimizer.h" #include "libraries.h" #include "messages.h" #include "amxmod_compat.h" plugin_info_t Plugin_info = { META_INTERFACE_VERSION, // ifvers "AMX Mod X", // name AMX_VERSION, // version __DATE__, // date "AMX Mod X Dev Team", // author "http://www.amxmodx.org", // url "AMXX", // logtag PT_ANYTIME, // (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; CLog g_log; CForwardMngr g_forwards; CList g_auth; CList g_cvars; CList g_forcemodels; CList g_forcesounds; CList g_forcegeneric; CPlayer g_players[33]; CPlayer* mPlayer; CPluginMngr g_plugins; CTaskMngr g_tasksMngr; CmdMngr g_commands; EventsMngr g_events; Grenades g_grenades; LogEventsMngr g_logevents; MenuMngr g_menucmds; CLangMngr g_langMngr; String g_log_dir; String g_mod_name; XVars g_xvars; 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; String 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; 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_cl_langs = {"amx_client_languages", "", FCVAR_SERVER}; cvar_t* amxmodx_version = NULL; cvar_t* amxmodx_modules = 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_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; 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 String *pString = new String(name); files.push(pString); #if !defined WIN32 } #endif } } void BuildPluginFileList(const char *initialdir, CStack & files) { char path[255]; #if defined WIN32 build_pathname_r(path, sizeof(path)-1, "%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__ build_pathname_r(path, sizeof(path)-1, "%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()) { String *pString = files.front(); snprintf(path, sizeof(path)-1, "%s/%s", initialdir, pString->c_str()); g_plugins.CALMFromFile(path); delete pString; files.pop(); } } void LoadExtraPluginsFromDir(const char *initialdir) { CStack files; char path[255]; BuildPluginFileList(initialdir, files); while (!files.empty()) { String *pString = files.front(); snprintf(path, sizeof(path)-1, "%s/%s", initialdir, pString->c_str()); 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(char *s) { if (!g_forcedmodules) { g_forcedmodules = true; for (CList::iterator a = g_forcemodels.begin(); a; ++a) { PRECACHE_MODEL((char*)(*a).getFilename()); ENGINE_FORCE_UNMODIFIED((*a).getForceType(), (*a).getMin(), (*a).getMax(), (*a).getFilename()); } } RETURN_META_VALUE(MRES_IGNORED, 0); } int C_PrecacheSound(char *s) { if (!g_forcedsounds) { g_forcedsounds = true; for (CList::iterator a = g_forcesounds.begin(); a; ++a) { PRECACHE_SOUND((char*)(*a).getFilename()); ENGINE_FORCE_UNMODIFIED((*a).getForceType(), (*a).getMin(), (*a).getMax(), (*a).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; } // 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"); 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"); char map_pluginsfile_path[256]; // ###### Load modules loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), PT_ANYTIME); g_plugins.CALMFromFile(get_localinfo("amxx_plugins", "addons/amxmodx/configs/plugins.ini")); LoadExtraPluginsToPCALM(get_localinfo("amxx_configsdir", "addons/amxmodx/configs")); snprintf(map_pluginsfile_path, sizeof(map_pluginsfile_path)-1, "%s/maps/plugins-%s.ini", get_localinfo("amxx_configsdir", "addons/amxmodx/configs"), 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, AMX_VERSION); char buffer[32]; sprintf(buffer, "%d", loaded); CVAR_SET_STRING(init_amxmodx_modules.name, buffer); // ###### Load Vault char file[255]; g_vault.setSource(build_pathname_r(file, sizeof(file) - 1, "%s", get_localinfo("amxx_vault", "addons/amxmodx/configs/vault.ini"))); g_vault.loadVault(); if (strlen(g_vault.get("server_language")) < 1) { g_vault.put("server_language", "en"); g_vault.saveVault(); } // ###### 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(get_localinfo("amxx_configsdir", "addons/amxmodx/configs")); g_plugins.loadPluginsFromFile(map_pluginsfile_path, 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_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_DONE); FF_ChangeLevel = registerForward("server_changelevel", ET_STOP, FP_STRING, FP_DONE); #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(); // ###### Call precache forward function g_dontprecache = false; executeForwards(FF_PluginPrecache); g_dontprecache = true; for (CList::iterator a = g_forcegeneric.begin(); a; ++a) { PRECACHE_GENERIC((char*)(*a).getFilename()); ENGINE_FORCE_UNMODIFIED((*a).getForceType(), (*a).getMin(), (*a).getMax(), (*a).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}, {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; } } } 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); // 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) executeForwards(FF_ClientDisconnect, static_cast(pPlayer->index)); if (pPlayer->ingame) { pPlayer->Disconnect(); --g_players_num; } } g_players_num = 0; executeForwards(FF_PluginEnd); RETURN_META(MRES_IGNORED); } extern CVector 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(); detachReloadModules(); 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(); ClearPluginLibraries(); modules_callPluginsUnloaded(); ClearMessages(); for (unsigned int i=0; itm_mon + 1, curTime->tm_mday, i); #ifdef __linux__ mkdir(build_pathname("%s", g_log_dir.c_str()), 0700); if (mkdir(build_pathname(buffer), 0700) < 0) #else mkdir(build_pathname("%s", g_log_dir.c_str())); 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.assign(buffer); // g_memreport_dir should be valid now break; } } m_dumpMemoryReport(build_pathname("%s/r%03d.txt", g_memreport_dir.c_str(), g_memreport_count)); AMXXLOG_Log("Memreport #%d created (file \"%s/r%03d.txt\") (interval %f)", g_memreport_count + 1, g_memreport_dir.c_str(), 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) { CPlayer** aa = new CPlayer*(pPlayer); if (aa) g_auth.put(aa); } else { pPlayer->Authorize(); if (g_auth_funcs.size()) { List::iterator iter, end=g_auth_funcs.end(); AUTHORIZEFUNC fn; const char* authid = GETPLAYERAUTHID(pEntity); for (iter=g_auth_funcs.begin(); iter!=end; iter++) { fn = (*iter); fn(pPlayer->index, authid); } } executeForwards(FF_ClientAuthorized, static_cast(pPlayer->index)); } } RETURN_META_VALUE(MRES_IGNORED, TRUE); } void C_ClientDisconnect(edict_t *pEntity) { CPlayer *pPlayer = GET_PLAYER_POINTER(pEntity); if (pPlayer->initialized) executeForwards(FF_ClientDisconnect, static_cast(pPlayer->index)); if (pPlayer->ingame) { --g_players_num; } pPlayer->Disconnect(); RETURN_META(MRES_IGNORED); } 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.assign(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(); if (g_auth_funcs.size()) { List::iterator iter, end=g_auth_funcs.end(); AUTHORIZEFUNC fn; const char* authid = GETPLAYERAUTHID(pEntity); for (iter=g_auth_funcs.begin(); iter!=end; iter++) { fn = (*iter); fn(pPlayer->index, authid); } } executeForwards(FF_ClientAuthorized, static_cast(pPlayer->index)); 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 \"Damaged Soul\" 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 ((pPlayer->menu > 0 && !pPlayer->vgui) && (gpGlobals->time > pPlayer->menuexpire)) { pPlayer->menu = 0; pPlayer->keys = 0; RETURN_META(MRES_SUPERCEDE); } int menuid = pPlayer->menu; pPlayer->menu = 0; MenuMngr::iterator a = g_menucmds.begin(); while (a) { if ((*a).matchCommand(menuid, bit_key) && (*a).getPlugin()->isExecutable((*a).getFunction())) { if (pPlayer->newmenu != -1) { int menu = pPlayer->newmenu; pPlayer->newmenu = -1; if (menu >= 0 && menu < (int)g_NewMenus.size()) { Menu *pMenu = g_NewMenus[menu]; int item = pMenu->PagekeyToItem(pPlayer->page, pressed_key+1); if (item == MENU_BACK) { pMenu->Display(pPlayer->index, pPlayer->page - 1); } else if (item == MENU_MORE) { pMenu->Display(pPlayer->index, pPlayer->page + 1); } else { ret = executeForwards((*a).getFunction(), static_cast(pPlayer->index), static_cast(menu), static_cast(item)); if (ret & 2) result = MRES_SUPERCEDE; else if (ret & 1) RETURN_META(MRES_SUPERCEDE); } } if (pPlayer->newmenu != -1) break; } else { 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); } } ++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; CList::iterator a = g_auth.begin(); while (a) { const char* auth = GETPLAYERAUTHID((*a)->pEdict); if ((auth == 0) || (*auth == 0)) { a.remove(); continue; } if (strcmp(auth, "STEAM_ID_PENDING")) { (*a)->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((*a)->index, auth); } } executeForwards(FF_ClientAuthorized, static_cast((*a)->index)); a.remove(); continue; } ++a; } } #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; #ifdef __linux__ 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); #ifdef __linux__ mkdir(build_pathname("%s", g_log_dir.c_str()), 0700); if (mkdir(build_pathname(buffer), 0700) < 0) #else mkdir(build_pathname("%s", g_log_dir.c_str())); 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.assign(buffer); // g_memreport_dir should be valid now break; } } m_dumpMemoryReport(build_pathname("%s/r%03d.txt", g_memreport_dir.c_str(), g_memreport_count)); AMXXLOG_Log("Memreport #%d created (file \"%s/r%03d.txt\") (interval %f)", g_memreport_count + 1, g_memreport_dir.c_str(), g_memreport_count, MEMREPORT_INTERVAL); g_memreport_count++; } #endif // MEMORY_TEST if (g_task_time > gpGlobals->time) RETURN_META(MRES_IGNORED); g_task_time = gpGlobals->time + 0.1f; g_tasksMngr.startFrame(); RETURN_META(MRES_IGNORED); } void C_MessageBegin_Post(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed) { if (ed) { if (gmsgBattery == msg_type && g_bmod_cstrike) { void* ptr = GET_PRIVATE(ed); #ifdef __linux__ int *z = (int*)ptr + 0x171; #else int *z = (int*)ptr + 0x16C; #endif int stop = (int)ed->v.armorvalue; *z = stop; ed->v.armorvalue = (float)stop; } 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 = pPlayer->thisTrace; pPlayer->thisTrace = ptr->vecEndPos; } RETURN_META(MRES_IGNORED); } void C_AlertMessage(ALERT_TYPE atype, 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(char *map, 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(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_cl_langs); amxmodx_version = CVAR_GET_POINTER(init_amxmodx_version.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.assign(a); if (g_mod_name.compare("cstrike") == 0 || g_mod_name.compare("czero") == 0 || g_mod_name.compare("dod") == 0) g_coloredmenus = true; else g_coloredmenus = false; // ###### Print short GPL print_srvconsole("\n AMX Mod X version %s Copyright (c) 2004-2006 AMX Mod X Development Team \n" " AMX Mod X comes with ABSOLUTELY NO WARRANTY; for details type `amxx gpl'.\n", AMX_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().c_str(), (char*)a.value().c_str()); ++a; } amx_config.clear(); } // ###### Initialize logging here g_log_dir.assign(get_localinfo("amxx_logs", "addons/amxmodx/logs")); // ###### 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); 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_cvars.clear(); g_langMngr.Clear(); ClearMessages(); modules_callPluginsUnloaded(); detachModules(); g_log.CloseFile(); Module_UncacheFunctions(); ClearLibraries(LibSource_Plugin); ClearLibraries(LibSource_Module); return (TRUE); } #ifdef __linux__ // linux prototype C_DLLEXPORT void GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals) { #else #ifdef _MSC_VER // MSVC: Simulate __stdcall calling convention C_DLLEXPORT __declspec(naked) void GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals) { __asm // Prolog { // Save ebp push ebp // Set stack frame pointer mov ebp, esp // Allocate space for local variables // The MSVC compiler gives us the needed size in __LOCAL_SIZE. sub esp, __LOCAL_SIZE // Push registers push ebx push esi push edi } #else // _MSC_VER #ifdef __GNUC__ // GCC can also work with this C_DLLEXPORT void __stdcall GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals) { #else // __GNUC__ // compiler not known #error There is no support (yet) for your compiler. Please use MSVC or GCC compilers or contact the AMX Mod X dev team. #endif // __GNUC__ #endif // _MSC_VER #endif // __linux__ // ** Function core <-- memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); gpGlobals = pGlobals; // --> ** Function core #ifdef _MSC_VER // Epilog if (sizeof(int*) == 8) { // 64 bit __asm { // Pop registers pop edi pop esi pop ebx // Restore stack frame pointer mov esp, ebp // Restore ebp pop ebp // 2 * sizeof(int*) = 16 on 64 bit ret 16 } } else { // 32 bit __asm { // Pop registers pop edi pop esi pop ebx // Restore stack frame pointer mov esp, ebp // Restore ebp pop ebp // 2 * sizeof(int*) = 8 on 32 bit ret 8 } } #endif // #ifdef _MSC_VER } 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; 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.c_str(), "cstrike") == 0 || stricmp(g_mod_name.c_str(), "czero") == 0) { meta_engfuncs.pfnSetModel = C_SetModel; g_bmod_cstrike = true; } else { g_bmod_cstrike = false; g_bmod_dod = !stricmp(g_mod_name.c_str(), "dod"); } 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