Admin Menu (SourceMod Scripting)

From AlliedModders Wiki
Revision as of 07:20, 21 April 2020 by Joinedsenses (talk | contribs) (Fix syntax)
Jump to: navigation, search

The admin menu is a menu available to all in-game administrators. It is a simple, one-level, category-based menu with heavy focus on consistency. The menu is simply a TopMenu, from the TopMenus extension, and is provided by adminmenu.sp.

The menu itself has no items unless external plugins provide them. Thus, it is possible (and recommended) for third party developers to extend the menu by attaching their own functionality to it. This requires a bit of patience, but this article documents most of the steps necessary.

Menu Organization

The menu is organized into categories and items. Categories are top-level selections on the menu. Items are selectable entries inside categories. There are two important rules:

  • There is no nesting. Categories cannot have sub-categories.
  • Only categories can exist at the top level (i.e. items must have a parent category).

All entries on the menu, be they categories or items, are called top menu objects, and each must have a unique name. For simplicity, the default admin menu entries use their respective command names as unique names, however this style is not required or enforced.

Unique names are used to identify items for sorting. For more information, see Admin Menu Configuration.


Interfacing to the Admin Menu

The admin menu plugin can be loaded or unloaded at any time, and accounting for this in your code is important. Generally, you must account for the following cases:

  • The admin menu is unloaded while you are interfaced to it.
  • The admin menu is loaded while your plugin is already loaded.
  • The admin menu is loaded before your plugin is loaded.

You do not need to remove menu items; they are removed automatically when your plugin is unloaded, and temporarily removed while your plugin is paused.

Example code is below:

#include <sourcemod>
 
/* Make the admin menu plugin optional */
#undef REQUIRE_PLUGIN
#include <adminmenu>
 
/* Keep track of the top menu */
TopMenu hAdminMenu = null;
 
public void OnPluginStart()
{
  /* See if the menu plugin is already ready */
  TopMenu topmenu;
  if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
  {
    /* If so, manually fire the callback */
    OnAdminMenuReady(topmenu);
  }
}
 
public void OnLibraryRemoved(const char[] name)
{
  if (StrEqual(name, "adminmenu", false))
  {
    hAdminMenu = null;
  }
}
 
public void OnAdminMenuReady(Handle aTopMenu)
{
  TopMenu topmenu = TopMenu.FromHandle(aTopMenu);
 
  /* Try to add the category first, if we want to add one.
     Leave this out, if you don't add a new category. */
  if (obj_dmcommands == INVALID_TOPMENUOBJECT)
  {
    OnAdminMenuCreated(topmenu);
  }
 
  /* Block us from being called twice */
  if (topmenu == hAdminMenu)
  {
    return;
  }
 
  hAdminMenu = topmenu;
 
  /* :TODO: Add everything to the menu! */
}

Top Menu Objects

Top Menu Objects are entries, either categories or items, on the admin menu (or any top menu). Each has the following properties:

  • ID (OUTPUT): The ID of the object in memory.
  • Name (INPUT): The unique name of the object.
  • Type (INPUT): Either a category or an item.
  • Handler (INPUT): The callback function.
  • Parent (INPUT): Empty for categories, or a category object ID for an item.
  • Override (INPUT): The name of the override associated with the command. For more information, see Overriding Command Access. Overrides don't need to be a command name.
  • Flags (INPUT): Default admin flags that should be associated with the object if the override is not set.

The callback determines how the object is drawn on the menu. If no flags are specified, the item is usable by any admin. If an admin does not have access to an object (including an entire category), it will not be displayed.


Adding Categories

Adding categories is sometimes beneficial for larger plugins with many commands. A category object receives two TopMenuAction callbacks:

  • TopMenuAction_DisplayOption - The category is being displayed.
  • TopMenuAction_DisplayTitle - The category is being displayed as a title.

For example, let's create a category called "CS:S DM Commands".

TopMenuObject obj_dmcommands;
 
public void OnAdminMenuCreated(Handle aTopMenu)
{
  Handle topmenu = TopMenu.FromHandle(aTopMenu);
 
  /* Block us from being called twice */
  if (topmenu == hAdminMenu && obj_dmcommands != INVALID_TOPMENUOBJECT)
  {
    return;
  }
 
  obj_dmcommands = AddToTopMenu(topmenu,
    "CS:S DM Commands",
    TopMenuObject_Category,
    CategoryHandler,
    INVALID_TOPMENUOBJECT);
}
 
public void CategoryHandler(TopMenu topmenu, 
      TopMenuAction action,
      TopMenuObject object_id,
      int param,
      char[] buffer,
      int maxlength)
{
  if (action == TopMenuAction_DisplayTitle)
  {
    Format(buffer, maxlength, "CS:S DM Commands:");
  }
  else if (action == TopMenuAction_DisplayOption)
  {
    Format(buffer, maxlength, "CS:S DM Commands");
  }
}

Note that a callback can handle more than one category. If that is the desired functionality, you can use the TopMenuObject value to see which object is being drawn.

Adding Items

Items are different from categories in that their selection can be detected. They must have a parent category. The code below shows an example of finding an existing category and adding an item to it.

void AttachAdminMenu()
{
  /* If the category is third party, it will have its own unique name. */
  TopMenuObject player_commands = FindTopMenuCategory(hAdminMenu, ADMINMENU_PLAYERCOMMANDS);
 
  if (player_commands == INVALID_TOPMENUOBJECT)
  {
    /* Error! */
    return;
  }
 
  AddToTopMenu(hAdminMenu, 
    "sm_poke",
    TopMenuObject_Item,
    AdminMenu_Poke,
    player_commands,
    "sm_poke",
    ADMFLAG_SLAY);
}
 
public void AdminMenu_Poke(TopMenu topmenu, 
      TopMenuAction action,
      TopMenuObject object_id,
      int param,
      char[] buffer,
      int maxlength)
{
  if (action == TopMenuAction_DisplayOption)
  {
    Format(buffer, maxlength, "Poke");
  }
  else if (action == TopMenuAction_SelectOption)
  {
    /* Do something! client who selected item is in param */
  }
}

Note that a callback can handle more than one item. If that is the desired functionality, you can use the TopMenuObject value to see which object is being drawn.

Returning to the Menu

Sometimes it is desirable to re-display the admin menu to the client. This can be achievied via RedisplayAdminMenu in adminmenu.inc.