Menus Explored, Part 2

Last article, we saw that menus needed to be separated into building and displaying, or else you end up with very limited, platform-specific code. How did we try to correct this with AMX Mod X’s new menu system?

The crux of the new menu system is in four functions:

  • menu_create/menu_destroy – Creates/destroys a new menu object.
  • menu_additem – Adds a selectable item to the menu.
  • menu_display – Displays a menu to a client.

Note that instead of generating the display string themselves, users can now add items one by one. The display function handles building the internal string for the user, as well as calculating the selectable key bitmask. The menu also handles pagination; if you add more than 10 items, the menu will be drawn with “Next/Back” options that work automatically.

How does the callback work? Instead of using the menu text string as a unique identifier, the menu handle itself is considered unique. Each new menu gets one and only one callback, and this is registered in menu_create. The only complicated part about this callback is calculating which item the user actually selected. If the user presses key ’5′ on page ’3′, the meaning of that 5 will change based on whether page 3 is at the middle or the end of the menu, how many items there are per page, and how many items were drawn on that particular page. Core does this by breaking the menu items into separate pages, and then remembering which page a user viewed. Since it doesn’t remember which items were on which page, it must make a positional calculation on every selection.

Why do menus work that way? It’s an optimization. Consider if menu_display destroyed the menu once the client was finished, or made a copy of the old menu, or something like that, just so the exact item list could be preserved even if the original was altered. This continual copying would be a performance hindrance, and suggesting to users that changing menus during display is a good idea would only lead to their own coding mistakes. Thus, we made the decision to keep menus “static” – you can add or edit items, but not remove them. Nonetheless, the actual item selection algorithm itself has been a near-endless source of “off by one” bugs, and we’ll see later that SourceMod does away with it.

The complications just start here. Next article, we’ll visit more issues that derived from AMX Mod X’s new menu system:

  • If you can’t edit the menu while it’s being displayed, how can you display one menu to multiple people and translate/disable certain items for each displayed instance? Do you have to create and destroy a new menu for each user?
  • If you destroy a menu, what happens if it’s currently being displayed to other players?
  • To avoid memory leaks, you need to call menu_destroy when you’re finished with a menu. But how can you destroy it if a player’s menu never finishes (disconnect, overridden, et cetera)?
  • If you display a menu to a player when a menu already exists for them, what happens?

Leave a Reply

You must be logged in to post a comment.