Admin Menu (SourceMod Scripting)

From AlliedModders Wiki
Revision as of 08:47, 10 February 2014 by Peace-Maker (talk | contribs) (Fixup example code)
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 */
new Handle:hAdminMenu = INVALID_HANDLE
 
public OnPluginStart()
{
	/* See if the menu plugin is already ready */
	new Handle:topmenu;
	if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
	{
		/* If so, manually fire the callback */
		OnAdminMenuReady(topmenu);
	}
}
 
public OnLibraryRemoved(const String:name[])
{
	if (StrEqual(name, "adminmenu"))
	{
		hAdminMenu = INVALID_HANDLE;
	}
}
 
public OnAdminMenuReady(Handle:topmenu)
{
	/* 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".

new TopMenuObject:obj_dmcommands;
 
public OnAdminMenuCreated(Handle:topmenu)
{
	/* Block us from being called twice */
	if (topmenu == hAdminMenu && obj_dmcommands != INVALID_TOPMENUOBJECT)
	{
		return;
	}
 
	obj_dmcommands = AddToTopMenu(hAdminMenu,
		"CS:S DM Commands",
		TopMenuObject_Category,
		CategoryHandler,
		INVALID_TOPMENUOBJECT);
}
 
public CategoryHandler(Handle:topmenu, 
			TopMenuAction:action,
			TopMenuObject:object_id,
			param,
			String:buffer[],
			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.

AttachAdminMenu()
{
	/* If the category is third party, it will have its own unique name. */
	new 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 AdminMenu_Poke(Handle:topmenu, 
			TopMenuAction:action,
			TopMenuObject:object_id,
			param,
			String:buffer[],
			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.