void CPlayer::Disconnect() { ingame = false; initialized = false; authorized = false; teamIdsInitialized = false; if (newmenu != -1) { Menu *pMenu = g_NewMenus[newmenu]; if (pMenu) { //prevent recursion newmenu = -1; menu = 0; executeForwards(pMenu->func, static_cast<cell>(ENTINDEX(pEdict)), static_cast<cell>(pMenu->thisId), static_cast<cell>(MENU_EXIT)); } } List<ClientCvarQuery_Info *>::iterator iter, end=queries.end(); for (iter=queries.begin(); iter!=end; iter++) { unregisterSPForward((*iter)->resultFwd); delete [] (*iter)->params; delete (*iter); } queries.clear(); menu = 0; newmenu = -1; }
static cell AMX_NATIVE_CALL menu_destroy(AMX *amx, cell *params) { GETMENU_R(params[1]); if (pMenu->isDestroying) { return 0; //prevent infinite recursion } pMenu->isDestroying = true; CPlayer *player; for (int i=1; i<=gpGlobals->maxClients; i++) { player = GET_PLAYER_POINTER_I(i); if (player->newmenu == pMenu->thisId) { player->newmenu = -1; player->menu = 0; executeForwards(pMenu->func, static_cast<cell>(i), static_cast<cell>(pMenu->thisId), static_cast<cell>(MENU_EXIT)); } } g_NewMenus[params[1]] = NULL; delete pMenu; g_MenuFreeStack.push(params[1]); return 1; }
void LogEventsMngr::executeLogEvents() { bool valid; for (CLogEvent* a = logevents[logArgc]; a; a = a->next) { valid = true; for (CLogEvent::LogCond* b = a->filters; b; b = b->next) { valid = false; for (CLogEvent::LogCondEle* c = b->list; c; c = c->next) { if (c->cmp->compareCondition(logArgs[b->argnum]) == 0) { valid = true; break; } } if (!valid) break; } if (valid) { executeForwards(a->func); } } }
static cell AMX_NATIVE_CALL menu_cancel(AMX *amx, cell *params) { int index = params[1]; if (index < 1 || index > gpGlobals->maxClients) { LogError(amx, AMX_ERR_NATIVE, "Player %d is not valid", index); return 0; } CPlayer *player = GET_PLAYER_POINTER_I(index); if (!player->ingame) { LogError(amx, AMX_ERR_NATIVE, "Played %d is not in game", index); return 0; } int menu = player->newmenu; if (menu < 0 || menu >= (int)g_NewMenus.size() || !g_NewMenus[menu]) return 0; Menu *pMenu = g_NewMenus[menu]; player->newmenu = -1; player->menu = 0; executeForwards(pMenu->func, static_cast<cell>(index), static_cast<cell>(pMenu->thisId), static_cast<cell>(MENU_EXIT)); return 1; }
// Pause a plugin void CPluginMngr::CPlugin::pausePlugin() { if (isValid()) { // call plugin_pause if provided if (m_PauseFwd != -1) executeForwards(m_PauseFwd); setStatus(ps_paused); } }
int SortArrayList(const void *elem1, const void *elem2) { return executeForwards( SortInfo.func, SortInfo.array_hndl, ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize, ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize, SortInfo.data, SortInfo.size ); }
int SortArrayListExCell(const void *elem1, const void *elem2) { size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize; size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize; return executeForwards( SortInfo.func, SortInfo.array_hndl, *&SortInfo.array_base[index1 * SortInfo.array_bsize], *&SortInfo.array_base[index2 * SortInfo.array_bsize], SortInfo.data, SortInfo.size ); }
// Unpause a plugin void CPluginMngr::CPlugin::unpausePlugin() { if (isValid() && (getStatusCode() != ps_stopped)) { // set status first so the function will be marked executable setStatus(ps_running); // call plugin_unpause if provided if (m_UnpauseFwd != -1) { executeForwards(m_UnpauseFwd); } } }
void plugin_srvcmd() { const char* cmd = CMD_ARGV(0); CmdMngr::iterator a = g_commands.srvcmdbegin(); while (a) { if ((*a).matchCommand(cmd) && (*a).getPlugin()->isExecutable((*a).getFunction())) { cell ret = executeForwards((*a).getFunction(), static_cast<cell>(g_srvindex), static_cast<cell>((*a).getFlags()), static_cast<cell>((*a).getId())); if (ret) break; } ++a; } }
// 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); }
//Builds the menu string for a specific page (set title to 0 to not include title) //page indices start at 0! static cell AMX_NATIVE_CALL menu_display(AMX *amx, cell *params) { GETMENU(params[2]); int player = params[1]; int page = params[3]; CPlayer* pPlayer = GET_PLAYER_POINTER_I(player); /* If the stupid handler keeps drawing menus, * We need to keep cancelling them. But we put in a quick infinite loop * counter to prevent this from going nuts. */ int menu; int loops = 0; while ((menu = pPlayer->newmenu) >= 0) { if ((size_t)menu >= g_NewMenus.size() || !g_NewMenus[menu]) { break; } Menu *pOther = g_NewMenus[menu]; pPlayer->newmenu = -1; pPlayer->menu = 0; executeForwards(pOther->func, static_cast<cell>(player), static_cast<cell>(pOther->thisId), static_cast<cell>(MENU_EXIT)); /* Infinite loop counter */ if (++loops >= 10) { LogError(amx, AMX_ERR_NATIVE, "Plugin called menu_display when item=MENU_EXIT"); return 0; } } // This will set the expire time of the menu to infinite pPlayer->menuexpire = INFINITE; return pMenu->Display(player, page); }
void Menu::Close(int player) { CPlayer *pPlayer = GET_PLAYER_POINTER_I(player); int status; if (gpGlobals->time > pPlayer->menuexpire) status = MENU_TIMEOUT; else status = MENU_EXIT; pPlayer->keys = 0; pPlayer->menu = 0; pPlayer->newmenu = -1; executeForwards(func, static_cast<cell>(player), static_cast<cell>(thisId), static_cast<cell>(status)); }
int SortArrayListExArray(const void *elem1, const void *elem2) { size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize; size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize; cell *addr1 = get_amxaddr(SortInfo.amx, SortInfo.addr1); cell *addr2 = get_amxaddr(SortInfo.amx, SortInfo.addr2); memcpy(addr1, &SortInfo.array_base[index1 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell)); memcpy(addr2, &SortInfo.array_base[index2 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell)); return executeForwards( SortInfo.func, SortInfo.array_hndl, SortInfo.addr1, SortInfo.addr2, SortInfo.data, SortInfo.size ); }
void EventsMngr::executeEvents() { static unsigned int reentrant = 0; if (!m_ParseFun) { return; } // Store old read data, which are either default values or previous event data int oldMsgType = m_ReadMsgType, oldReadPos = m_ReadPos; MsgDataEntry *oldReadVault = m_ReadVault, *readVault = NULL; // We have a re-entrant call if (reentrant++) { // Create temporary read vault readVault = new MsgDataEntry[m_ParsePos + 1]; m_ReadVault = readVault; } else if (m_ReadVaultSize != m_ParseVaultSize) { // Extend read vault size if necessary delete [] m_ReadVault; m_ReadVault = new MsgDataEntry[m_ParseVaultSize]; m_ReadVaultSize = m_ParseVaultSize; // Update old read vault so we don't restore to a wrong pointer oldReadVault = m_ReadVault; } // Copy data over to readvault m_ReadPos = m_ParsePos; m_ReadMsgType = m_ParseMsgType; if (m_ParseVault) { memcpy(m_ReadVault, m_ParseVault, (m_ParsePos + 1) * sizeof(MsgDataEntry)); } // Reset this here so we don't trigger re-entrancy for unregistered messages ClEventVec *parseFun = m_ParseFun; m_ParseFun = NULL; for (ClEventVecIter iter = parseFun->begin(); iter; ++iter) { if ((*iter).m_Done) { (*iter).m_Done = false; continue; } (*iter).m_Stamp = (float)*m_Timer; executeForwards((*iter).m_Func, static_cast<cell>(m_ReadVault ? m_ReadVault[0].iValue : 0)); } // Restore old read data, either resetting to default or to previous event data m_ReadMsgType = oldMsgType; m_ReadPos = oldReadPos; m_ReadVault = oldReadVault; delete [] readVault; --reentrant; }
DETOUR_DECL_STATIC2(Cvar_DirectSet, void, struct cvar_s*, var, const char*, value) { CvarInfo* info = nullptr; if (!var || !value // Sanity checks against bogus pointers. || strcmp(var->string, value) == 0 // Make sure old and new values are different to not trigger callbacks. || !g_CvarManager.CacheLookup(var->name, &info)) // No data in cache, nothing to do. { DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); return; } if (info->bound.hasMin || info->bound.hasMax) // cvar_s doesn't have min/max mechanism, so we check things here. { float fvalue = atof(value); bool oob = false; if (info->bound.hasMin && fvalue < info->bound.minVal) { oob = true; fvalue = info->bound.minVal; } else if (info->bound.hasMax && fvalue > info->bound.maxVal) { oob = true; fvalue = info->bound.maxVal; } if (oob) // Found value out of bound, set new value and block original call. { CVAR_SET_FLOAT(var->name, fvalue); return; } } ke::AString oldValue; // We save old value since it will be likely changed after original function called. if (!info->hooks.empty()) { oldValue = var->string; } DETOUR_STATIC_CALL(Cvar_DirectSet)(var, value); if (!info->binds.empty()) { for (size_t i = 0; i < info->binds.length(); ++i) { CvarBind* bind = info->binds[i]; switch (bind->type) { case CvarBind::CvarType_Int: { *bind->varAddress = atoi(var->string); break; } case CvarBind::CvarType_Float: { float fvalue = atof(var->string); *bind->varAddress = amx_ftoc(fvalue); break; } case CvarBind::CvarType_String: { set_amxstring_simple(bind->varAddress, var->string, bind->varLength); break; } } } } if (!info->hooks.empty()) { for (size_t i = 0; i < info->hooks.length(); ++i) { CvarHook* hook = info->hooks[i]; if (hook->forward->state == AutoForward::FSTATE_OK) // Our callback can be enable/disabled by natives. { executeForwards(hook->forward->id, reinterpret_cast<cvar_t*>(var), oldValue.chars(), var->string); } } } }
// 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(); 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[255]; g_vault.setSource(build_pathname_r(file, sizeof(file) - 1, "%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 (CList<ForceObject>::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); }
const char *Menu::GetTextString(int player, page_t page, int &keys) { page_t pages = GetPageCount(); item_t numItems = GetItemCount(); if (page >= pages) return NULL; m_Text = nullptr; char buffer[255]; if (items_per_page && (pages != 1)) { if (m_AutoColors) ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s %d/%d\n\\w\n", m_Title.chars(), page + 1, pages); else ke::SafeSprintf(buffer, sizeof(buffer), "%s %d/%d\n\n", m_Title.chars(), page + 1, pages); } else { if (m_AutoColors) ke::SafeSprintf(buffer, sizeof(buffer), "\\y%s\n\\w\n", m_Title.chars()); else ke::SafeSprintf(buffer, sizeof(buffer), "%s\n\n", m_Title.chars()); } m_Text = m_Text + buffer; enum { Display_Back = (1<<0), Display_Next = (1<<1), }; int flags = Display_Back|Display_Next; item_t start = page * items_per_page; item_t end = 0; if (items_per_page) { if (start + items_per_page >= numItems) { end = numItems; flags &= ~Display_Next; } else { end = start + items_per_page; } } else { end = numItems; if (end > 10) { end = 10; } } if (page == 0) { flags &= ~Display_Back; } menuitem *pItem = NULL; int option = 0; keys = 0; bool enabled = true; int ret = 0; int slots = 0; int option_display = 0; for (item_t i = start; i < end; i++) { // reset enabled enabled = true; pItem = m_Items[i]; if (pItem->access && !(pItem->access & g_players[player].flags[0])) { enabled = false; } if (pItem->handler != -1) { ret = executeForwards(pItem->handler, static_cast<cell>(player), static_cast<cell>(thisId), static_cast<cell>(i)); if (ret == ITEM_ENABLED) { enabled = true; } else if (ret == ITEM_DISABLED) { enabled = false; } } if (pItem->pfn) { ret = (pItem->pfn)(player, thisId, i); if (ret == ITEM_ENABLED) { enabled = true; } else if (ret == ITEM_DISABLED) { enabled = false; } } if (pItem->isBlank) { enabled = false; } if (enabled) { keys |= (1<<option); } option_display = ++option; if (option_display == 10) { option_display = 0; } if (pItem->isBlank) { ke::SafeSprintf(buffer, sizeof(buffer), "%s\n", pItem->name.chars()); } else if (enabled) { if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "%s%d.\\w %s\n", m_ItemColor.chars(),option_display, pItem->name.chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option_display, pItem->name.chars()); } } else { if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option_display, pItem->name.chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", pItem->name.chars()); } } slots++; m_Text = m_Text + buffer; //attach blanks if (pItem->blanks.length()) { for (size_t j=0; j<pItem->blanks.length(); j++) { if (pItem->blanks[j].EatNumber()) { option++; } m_Text = m_Text + pItem->blanks[j].GetDisplay(); m_Text = m_Text + "\n"; slots++; } } } if (items_per_page) { /* Pad spaces until we reach the end of the max possible items */ for (unsigned int i=(unsigned)slots; i<items_per_page; i++) { m_Text = m_Text + "\n"; option++; } /* Make sure there is at least one visual pad */ m_Text = m_Text + "\n"; /* Don't bother if there is only one page */ if (pages > 1) { if (flags & Display_Back) { keys |= (1<<option++); if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "%s%d. \\w%s\n", m_ItemColor.chars(), option == 10 ? 0 : option, m_OptNames[abs(MENU_BACK)].chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option == 10 ? 0 : option, m_OptNames[abs(MENU_BACK)].chars()); } } else { option++; if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option == 10 ? 0 : option, m_OptNames[abs(MENU_BACK)].chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", m_OptNames[abs(MENU_BACK)].chars()); } } m_Text = m_Text + buffer; if (flags & Display_Next) { keys |= (1<<option++); if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "%s%d. \\w%s\n", m_ItemColor.chars(), option == 10 ? 0 : option, m_OptNames[abs(MENU_MORE)].chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option == 10 ? 0 : option, m_OptNames[abs(MENU_MORE)].chars()); } } else { option++; if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "\\d%d. %s\n\\w", option == 10 ? 0 : option, m_OptNames[abs(MENU_MORE)].chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "#. %s\n", m_OptNames[abs(MENU_MORE)].chars()); } } m_Text = m_Text + buffer; } else { /* Keep padding */ option += 2; } } if ((items_per_page && !m_NeverExit) || (m_ForceExit && numItems < 10)) { /* Visual pad has not been added yet */ if (!items_per_page) m_Text = m_Text + "\n"; keys |= (1<<option++); if (m_AutoColors) { ke::SafeSprintf(buffer, sizeof(buffer), "%s%d. \\w%s\n", m_ItemColor.chars(), option == 10 ? 0 : option, m_OptNames[abs(MENU_EXIT)].chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s\n", option == 10 ? 0 : option, m_OptNames[abs(MENU_EXIT)].chars()); } m_Text = m_Text + buffer; } return m_Text.ptr(); }
// UTIL_FakeClientCommand // PURPOSE: Sends a fake client command to GameDLL // HOW DOES IT WORK: // 1) Stores command and arguments into a global and sets the global "fake" flag to true // 2) Invokes ClientCommand in GameDLL // 3) meta_api.cpp overrides Cmd_Args, Cmd_Argv, Cmd_Argc and gives them fake values if the "fake" flag is set // 4) unsets the global "fake" flag void UTIL_FakeClientCommand(edict_t *pEdict, const char *cmd, const char *arg1, const char *arg2, bool fwd) { if (!cmd) return; // no command // store command g_fakecmd.argv[0] = cmd; // if only arg2 is passed, swap the arguments if (!arg1 && arg2) { arg1 = arg2; arg2 = NULL; } // store arguments if (arg2) { // both arguments passed g_fakecmd.argc = 3; // 2 arguments + 1 command // store arguments g_fakecmd.argv[1] = arg1; g_fakecmd.argv[2] = arg2; // build argument line ke::SafeSprintf(g_fakecmd.args, sizeof(g_fakecmd.args), "%s %s", arg1, arg2); } else if (arg1) { // only one argument passed g_fakecmd.argc = 2; // 1 argument + 1 command // store argument g_fakecmd.argv[1] = arg1; // build argument line ke::SafeSprintf(g_fakecmd.args, sizeof(g_fakecmd.args), "%s", arg1); } else g_fakecmd.argc = 1; // no argmuents -> only one command /* Notify plugins about this command */ if (fwd) { /* Set flag so read_argc/v/s functions will give proper value */ g_fakecmd.notify = true; if (executeForwards(FF_ClientCommand, static_cast<cell>(GET_PLAYER_POINTER(pEdict)->index)) > 0) { g_fakecmd.notify = false; return; } /* 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, arg1) && (*aa).getPlugin()->isExecutable((*aa).getFunction())) { if (executeForwards((*aa).getFunction(), static_cast<cell>(GET_PLAYER_POINTER(pEdict)->index)), static_cast<cell>((*aa).getFlags()), static_cast<cell>((*aa).getId()) > 0) { g_fakecmd.notify = false; return; } } ++aa; } /* Unset flag */ g_fakecmd.notify = false; } // set the global "fake" flag so the Cmd_Arg* functions will be superceded g_fakecmd.fake = true; // tell the GameDLL that the client sent a command MDLL_ClientCommand(pEdict); // unset the global "fake" flag g_fakecmd.fake = false; }