Menus Explored, Part 3

Last article, we saw that AMX Mod X’s new menus allowed for separate menu building objects, which could then be displayed to clients. This display-agnostic system makes mod support, pagination, and item selection calculation much easier for developers.

The first question is a crucial one. Let’s say we want to translate a menu differently for each player. Do we:

  1. Create a different menu for each player and then destroy it, or;
  2. Alter the menu after each display?

The answer is neither. The new menu system has item callbacks, which are called before drawing each item. In these callbacks, developers can dynamically change how their menu is displayed per-client, without altering the menu itself. In here, the item’s display text can be altered, or the item’s enabled/disabled status can be toggled. When we start discussing SourceMod’s menu system, we’ll see that this concept totally breaks down when we combine it with AMX Mod X’s pagination and item selection algorithms.

What happens if we destroy a menu that’s being displayed? The menu gets removed from every player. While it may linger on the screen, Core must remove all references to it.

What happens if a menu gets displayed over another menu? AMX Mod X does not queue menus. Next article we’ll take a look at a system that does.

The most important question of all is: what happens if a player doesn’t select an item? If the menu gets overridden, canceled, the client disconnected, or the menu times out, the user must have some way of knowing so they can call menu_destroy. The menu handler has a special case for this in which the callback gets an invalid item, -3, which means “the menu has been exited.” Thus, there are two burdens here:

  • Core must guarantee that if the menu is ever “lost” somehow, that the callback will always be given an exit code. Otherwise, it will memory leak.
  • There is a burden on developers to make sure that they call menu_destroy on temporary menus both in the case of a valid item and an exit code.

The situation gets even murkier. Let’s say we have the following pseudo-code:

show(client)
{
   new menu = menu_create("My Menu", "MyHandler")
   menu_display(menu, client)
   menu_destroy(menu)
}
 
public MyHandler(menu, client, item)
{
   if (item == MENU_EXIT)
   {
      menu_destroy(menu)
   } else {
      /* item code */
      menu_destroy(menu)
   }
}

In this example, we’ve destroyed a menu while it’s being displayed. This triggers the menu’s handler with an exit code. However, the code has no way of knowing that the menu is already being destroyed, and thus must assume it’s safe to destroy the menu. Although this re-entrancy problem is mitigated with menu_cancel, it still exists, and it must be made safe in Core.

In conclusion, we can see that the AMX Mod X new menu system is still flawed. It has subtle re-entrancy issues stemming from a limited callback design. It has complicated case scenarios about the current state of a menu and its relation to players. It does not support queuing. The item selection calculations tend to be expensive and buggy, as it has to account for which page the player was viewing. And lastly, as we’ll see later, the pagination system itself does not really work in advanced cases.

Next article, we’ll go into another menu system I designed for a proprietary system at the same time. After that, we’ll dive into SourceMod menus.

Leave a Reply

You must be logged in to post a comment.