From 387dc6a1882b3362f51cd2c8f9b71aa8596b674f Mon Sep 17 00:00:00 2001 From: Vincent Herbet Date: Tue, 10 Jul 2018 12:09:20 +0200 Subject: [PATCH] Fix a menu recursion issue (#471) * Factorize menu close logic in show_menu() * Use CS-specific player's m_iMenu offset instead of menuselect command --- amxmodx/CGameConfigs.h | 13 +++++++++ amxmodx/amxmodx.cpp | 65 +++++++++++++++++++++--------------------- amxmodx/newmenus.cpp | 11 +++---- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/amxmodx/CGameConfigs.h b/amxmodx/CGameConfigs.h index 72f31358..3ced7691 100644 --- a/amxmodx/CGameConfigs.h +++ b/amxmodx/CGameConfigs.h @@ -166,6 +166,19 @@ class CGameConfigManager : public IGameConfigManager StringHashMap m_customHandlers; }; +#define GET_OFFSET(classname, member) \ + static int member = -1; \ + if (member == -1) \ + { \ + TypeDescription type; \ + if (!CommonConfig->GetOffsetByClass(classname, #member, &type) || type.fieldOffset < 0)\ + { \ + LogError(amx, AMX_ERR_NATIVE, "Invalid %s offset. Native %s is disabled", #member, __FUNCTION__);\ + return 0; \ + } \ + member = type.fieldOffset; \ + } + extern CGameConfigManager ConfigManager; extern IGameConfig *CommonConfig; diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index ee0eac70..2cfeec5e 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -1246,31 +1246,48 @@ static cell AMX_NATIVE_CALL get_user_team(AMX *amx, cell *params) /* 3 param */ static cell AMX_NATIVE_CALL show_menu(AMX *amx, cell *params) /* 3 param */ { + auto closeMenu = [amx](int index) -> int + { + auto pPlayer = GET_PLAYER_POINTER_I(index); + + if (!pPlayer->ingame) + { + return 1; + } + + pPlayer->keys = 0; + pPlayer->menu = 0; + + // Fire newmenu callback so closing it can be handled by the plugin + if (!CloseNewMenus(pPlayer)) + { + LogError(amx, AMX_ERR_NATIVE, "Plugin called menu_display when item=MENU_EXIT"); + return 2; + } + + if (g_bmod_cstrike) + { + GET_OFFSET("CBasePlayer", m_iMenu); + set_pdata(pPlayer->pEdict, m_iMenu, 0); + } + + return 0; + }; + + int index = params[1]; + // If show_menu is called from within a newmenu callback upon receiving MENU_EXIT // it is possible for this native to recurse. We need to close newmenus right away // because the recursive call would otherwise modify/corrupt the static get_amxstring // buffer mid execution. This will either display incorrect text or result in UTIL_ShowMenu // running into an infinite loop. - int index = params[1]; if (index == 0) { for (int i = 1; i <= gpGlobals->maxClients; ++i) { - CPlayer* pPlayer = GET_PLAYER_POINTER_I(i); - - if (pPlayer->ingame) + if (closeMenu(i) == 2) { - pPlayer->keys = 0; - pPlayer->menu = 0; - - // Fire newmenu callback so closing it can be handled by the plugin - if (!CloseNewMenus(pPlayer)) - { - LogError(amx, AMX_ERR_NATIVE, "Plugin called menu_display when item=MENU_EXIT"); - return 0; - } - - UTIL_FakeClientCommand(pPlayer->pEdict, "menuselect", "10", 0); + return 0; } } } @@ -1282,23 +1299,7 @@ static cell AMX_NATIVE_CALL show_menu(AMX *amx, cell *params) /* 3 param */ return 0; } - CPlayer* pPlayer = GET_PLAYER_POINTER_I(index); - - if (pPlayer->ingame) - { - pPlayer->keys = 0; - pPlayer->menu = 0; - - // Fire newmenu callback so closing it can be handled by the plugin - if (!CloseNewMenus(pPlayer)) - { - LogError(amx, AMX_ERR_NATIVE, "Plugin called menu_display when item=MENU_EXIT"); - return 0; - } - - UTIL_FakeClientCommand(pPlayer->pEdict, "menuselect", "10", 0); - } - else + if (closeMenu(index)) { return 0; } diff --git a/amxmodx/newmenus.cpp b/amxmodx/newmenus.cpp index 478be49e..5e6d0773 100755 --- a/amxmodx/newmenus.cpp +++ b/amxmodx/newmenus.cpp @@ -316,11 +316,6 @@ bool Menu::Display(int player, page_t page) CPlayer *pPlayer = GET_PLAYER_POINTER_I(player); - pPlayer->keys = 0; - pPlayer->menu = 0; - - UTIL_FakeClientCommand(pPlayer->pEdict, "menuselect", "10", 0); - pPlayer->keys = keys; pPlayer->menu = menuId; pPlayer->newmenu = thisId; @@ -828,6 +823,12 @@ static cell AMX_NATIVE_CALL menu_display(AMX *amx, cell *params) return 0; } + if (g_bmod_cstrike) + { + GET_OFFSET("CBasePlayer", m_iMenu); + set_pdata(pPlayer->pEdict, m_iMenu, 0); + } + int time = -1; if (params[0] / sizeof(cell) >= 4) time = params[4];