diff --git a/amxmodx/CMenu.cpp b/amxmodx/CMenu.cpp index 69fc254e..7fb8b130 100755 --- a/amxmodx/CMenu.cpp +++ b/amxmodx/CMenu.cpp @@ -35,13 +35,14 @@ // ***************************************************** // class MenuMngr // ***************************************************** -MenuMngr::MenuCommand::MenuCommand(CPluginMngr::CPlugin *a, int mi, int k, int f) +MenuMngr::MenuCommand::MenuCommand(CPluginMngr::CPlugin *a, int mi, int k, int f, int n) { plugin = a; keys = k; menuid = mi; function = f; next = 0; + newmenu = n; } MenuMngr::~MenuMngr() @@ -105,6 +106,33 @@ void MenuMngr::removeMenuId(int id) } } +void MenuMngr::removeMenuCmds(int newMenu) +{ + MenuCommand *c = headcmd; + MenuCommand *lc = NULL; + MenuCommand *tmp; + while (c) + { + if (c->newmenu == newMenu) + { + if (m_watch_iter.a == c) + { + ++m_watch_iter; + } + if (lc) + lc->next = c->next; + else + headcmd = c->next; + tmp = c->next; + delete c; + c = tmp; + } else { + lc = c; + c = c->next; + } + } +} + int MenuMngr::registerMenuId(const char* n, AMX* a) { int id = findMenuId(n, a); @@ -120,11 +148,11 @@ int MenuMngr::registerMenuId(const char* n, AMX* a) return headid->id; } -void MenuMngr::registerMenuCmd(CPluginMngr::CPlugin *a, int mi, int k, int f) +void MenuMngr::registerMenuCmd(CPluginMngr::CPlugin *a, int mi, int k, int f, int n) { MenuCommand** temp = &headcmd; while (*temp) temp = &(*temp)->next; - *temp = new MenuCommand(a, mi, k, f); + *temp = new MenuCommand(a, mi, k, f, n); } void MenuMngr::clear() diff --git a/amxmodx/CMenu.h b/amxmodx/CMenu.h index ca27325c..f4c8242c 100755 --- a/amxmodx/CMenu.h +++ b/amxmodx/CMenu.h @@ -68,11 +68,12 @@ private: int function; MenuCommand* next; - MenuCommand(CPluginMngr::CPlugin *a, int mi, int k, int f); + MenuCommand(CPluginMngr::CPlugin *a, int mi, int k, int f, int n=-1); public: inline int getFunction() { return function; } inline CPluginMngr::CPlugin* getPlugin() { return plugin; } inline bool matchCommand(int m, int k) { return ((m == menuid) && (keys & k)); } + int newmenu; } *headcmd; public: @@ -85,7 +86,8 @@ public: int findMenuId(const char* name, AMX* a = 0); int registerMenuId(const char* n, AMX* a); void removeMenuId(int id); - void registerMenuCmd(CPluginMngr::CPlugin *a, int mi, int k, int f); + void removeMenuCmds(int newMenu); + void registerMenuCmd(CPluginMngr::CPlugin *a, int mi, int k, int f, int n=-1); void clear(); class iterator diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 5d41705a..79842def 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -948,7 +948,7 @@ void C_ClientCommand(edict_t *pEntity) g_menucmds.SetWatchIter(a); if ((*a).matchCommand(menuid, bit_key) && (*a).getPlugin()->isExecutable((*a).getFunction())) { - if (pPlayer->newmenu != -1) + if (pPlayer->newmenu != -1 && pPlayer->newmenu == (*a).newmenu) { int menu = pPlayer->newmenu; pPlayer->newmenu = -1; diff --git a/amxmodx/newmenus.cpp b/amxmodx/newmenus.cpp index cdb879e5..3c9e63d2 100755 --- a/amxmodx/newmenus.cpp +++ b/amxmodx/newmenus.cpp @@ -525,7 +525,6 @@ static cell AMX_NATIVE_CALL menu_create(AMX *amx, cell *params) } int id = g_menucmds.registerMenuId(title, amx); - g_menucmds.registerMenuCmd(g_plugins.findPluginFast(amx), id, 1023, func); Menu *pMenu = new Menu(title, id, 0); @@ -535,14 +534,16 @@ static cell AMX_NATIVE_CALL menu_create(AMX *amx, cell *params) { g_NewMenus.push_back(pMenu); pMenu->thisId = (int)g_NewMenus.size() - 1; - return (int)g_NewMenus.size() - 1; } else { int pos = g_MenuFreeStack.front(); g_MenuFreeStack.pop(); g_NewMenus[pos] = pMenu; pMenu->thisId = pos; - return pos; } + + g_menucmds.registerMenuCmd(g_plugins.findPluginFast(amx), id, 1023, func, pMenu->thisId); + + return pMenu->thisId; } static cell AMX_NATIVE_CALL menu_addblank(AMX *amx, cell *params) @@ -622,6 +623,36 @@ static cell AMX_NATIVE_CALL menu_display(AMX *amx, cell *params) 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(player), + static_cast(pOther->thisId), + static_cast(MENU_EXIT)); + + /* Infinite loop counter */ + if (++loops >= 10) + { + LogError(amx, AMX_ERR_NATIVE, "Recursion bug detected in menu handler"); + return 0; + } + } + // This will set the expire time of the menu to infinite pPlayer->menuexpire = INFINITE; @@ -892,7 +923,27 @@ static cell AMX_NATIVE_CALL menu_destroy(AMX *amx, cell *params) } pMenu->isDestroying = true; - g_menucmds.removeMenuId(pMenu->menuId); + + /* :TODO: Move this to some sort of referencing counting thingy */ + bool removeMenuId = true; + for (size_t i = 0; i < g_NewMenus.size(); i++) + { + if (!g_NewMenus[i] || g_NewMenus[i]->isDestroying) + { + continue; + } + if (g_NewMenus[i]->menuId == pMenu->menuId) + { + removeMenuId = false; + break; + } + } + if (removeMenuId) + { + g_menucmds.removeMenuId(pMenu->menuId); + } + g_menucmds.removeMenuCmds(pMenu->thisId); + CPlayer *player; for (int i=1; i<=gpGlobals->maxClients; i++) { diff --git a/plugins/testsuite/menutest.sma b/plugins/testsuite/menutest.sma index 5882e95d..97c292af 100644 --- a/plugins/testsuite/menutest.sma +++ b/plugins/testsuite/menutest.sma @@ -55,6 +55,8 @@ public Test_Menu1_Handler(id, menu, item) client_print(id, print_chat, "Menu resolved to: %s (%s)", name, cmd) + menu_destroy(menu) + return PLUGIN_HANDLED }