Introduces a new documentation site for CS2-SimpleAdmin using Docusaurus, including developer API references, tutorials, user guides, and module documentation. Removes the CleanModule example and updates FunCommands and ExampleModule. Also updates main plugin and API files to support new documentation and module structure.
16 KiB
sidebar_position
| sidebar_position |
|---|
| 3 |
Menus API
Complete reference for creating interactive admin menus.
Menu Categories
RegisterMenuCategory
Register a new menu category in the admin menu.
void RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic")
Parameters:
categoryId- Unique identifier for the categorycategoryName- Display name shown in menupermission- Required permission to see category (default: "@css/generic")
Example:
_api.RegisterMenuCategory("mycategory", "My Custom Category", "@css/generic");
Best Practice:
Register categories in the OnSimpleAdminReady event handler:
_api.OnSimpleAdminReady += () =>
{
_api.RegisterMenuCategory("mycategory", Localizer?["category_name"] ?? "My Category");
};
Menu Registration
RegisterMenu (Basic)
Register a menu within a category.
void RegisterMenu(
string categoryId,
string menuId,
string menuName,
Func<CCSPlayerController, object> menuFactory,
string? permission = null,
string? commandName = null
)
Parameters:
categoryId- Category to add menu tomenuId- Unique menu identifiermenuName- Display name in menumenuFactory- Function that creates the menupermission- Required permission (optional)commandName- Command for permission override (optional, e.g., "css_god")
Example:
_api.RegisterMenu(
"mycategory",
"mymenu",
"My Menu",
CreateMyMenu,
"@css/generic"
);
private object CreateMyMenu(CCSPlayerController player)
{
var menu = _api!.CreateMenuWithBack("My Menu", "mycategory", player);
// Add options...
return menu;
}
RegisterMenu (with MenuContext) ⭐ RECOMMENDED
Register a menu with automatic context passing - eliminates duplication!
void RegisterMenu(
string categoryId,
string menuId,
string menuName,
Func<CCSPlayerController, MenuContext, object> menuFactory,
string? permission = null,
string? commandName = null
)
Parameters:
categoryId- Category to add menu tomenuId- Unique menu identifiermenuName- Display name in menumenuFactory- Function that receives player AND contextpermission- Required permission (optional)commandName- Command for permission override (optional)
Example:
// ✅ NEW WAY - No duplication!
_api.RegisterMenu(
"fun",
"god",
"God Mode",
CreateGodMenu,
"@css/cheats",
"css_god"
);
private object CreateGodMenu(CCSPlayerController admin, MenuContext context)
{
// context contains: CategoryId, MenuId, MenuTitle, Permission, CommandName
return _api!.CreateMenuWithPlayers(
context, // ← Automatically uses "God Mode" and "fun"
admin,
player => player.IsValid && admin.CanTarget(player),
(admin, target) => ToggleGod(admin, target)
);
}
// ❌ OLD WAY - Had to repeat "God Mode" and "fun"
private object CreateGodMenuOld(CCSPlayerController admin)
{
return _api!.CreateMenuWithPlayers(
"God Mode", // ← Repeated from RegisterMenu
"fun", // ← Repeated from RegisterMenu
admin,
filter,
action
);
}
MenuContext Properties:
public class MenuContext
{
public string CategoryId { get; } // e.g., "fun"
public string MenuId { get; } // e.g., "god"
public string MenuTitle { get; } // e.g., "God Mode"
public string? Permission { get; } // e.g., "@css/cheats"
public string? CommandName { get; } // e.g., "css_god"
}
UnregisterMenu
Remove a menu from a category.
void UnregisterMenu(string categoryId, string menuId)
Example:
public override void Unload(bool hotReload)
{
_api?.UnregisterMenu("mycategory", "mymenu");
}
Menu Creation
CreateMenuWithBack
Create a menu with automatic back button.
object CreateMenuWithBack(string title, string categoryId, CCSPlayerController player)
Parameters:
title- Menu titlecategoryId- Category for back button navigationplayer- Player viewing the menu
Example:
var menu = _api!.CreateMenuWithBack("My Menu", "mycategory", admin);
_api.AddMenuOption(menu, "Option 1", _ => DoAction1());
_api.AddMenuOption(menu, "Option 2", _ => DoAction2());
return menu;
CreateMenuWithBack (with Context) ⭐ RECOMMENDED
Create a menu using context - no duplication!
object CreateMenuWithBack(MenuContext context, CCSPlayerController player)
Example:
private object CreateMenu(CCSPlayerController admin, MenuContext context)
{
// ✅ Uses context.MenuTitle and context.CategoryId automatically
var menu = _api!.CreateMenuWithBack(context, admin);
_api.AddMenuOption(menu, "Option 1", _ => DoAction1());
return menu;
}
CreateMenuWithPlayers
Create a menu showing a list of players.
object CreateMenuWithPlayers(
string title,
string categoryId,
CCSPlayerController admin,
Func<CCSPlayerController, bool> filter,
Action<CCSPlayerController, CCSPlayerController> onSelect
)
Parameters:
title- Menu titlecategoryId- Category for back buttonadmin- Admin viewing menufilter- Function to filter which players to showonSelect- Action when player is selected (admin, selectedPlayer)
Example:
return _api!.CreateMenuWithPlayers(
"Select Player",
"mycategory",
admin,
player => player.IsValid && admin.CanTarget(player),
(admin, target) =>
{
// Do something with selected player
DoAction(admin, target);
}
);
CreateMenuWithPlayers (with Context) ⭐ RECOMMENDED
object CreateMenuWithPlayers(
MenuContext context,
CCSPlayerController admin,
Func<CCSPlayerController, bool> filter,
Action<CCSPlayerController, CCSPlayerController> onSelect
)
Example:
private object CreatePlayerMenu(CCSPlayerController admin, MenuContext context)
{
return _api!.CreateMenuWithPlayers(
context, // ← Automatically uses correct title and category!
admin,
player => player.PawnIsAlive && admin.CanTarget(player),
(admin, target) => PerformAction(admin, target)
);
}
Menu Options
AddMenuOption
Add a clickable option to a menu.
void AddMenuOption(
object menu,
string name,
Action<CCSPlayerController> action,
bool disabled = false,
string? permission = null
)
Parameters:
menu- Menu to add option toname- Option display textaction- Function called when selecteddisabled- Whether option is disabled (default: false)permission- Required permission to see option (optional)
Example:
var menu = _api!.CreateMenuWithBack("Actions", "mycategory", admin);
_api.AddMenuOption(menu, "Heal Player", _ =>
{
target.SetHp(100);
});
_api.AddMenuOption(menu, "Admin Only Option", _ =>
{
// Admin action
}, false, "@css/root");
return menu;
AddSubMenu
Add a submenu option that opens another menu.
void AddSubMenu(
object menu,
string name,
Func<CCSPlayerController, object> subMenuFactory,
bool disabled = false,
string? permission = null
)
Parameters:
menu- Parent menuname- Submenu option display textsubMenuFactory- Function that creates the submenudisabled- Whether option is disabled (default: false)permission- Required permission (optional)
Example:
var menu = _api!.CreateMenuWithBack("Main Menu", "mycategory", admin);
_api.AddSubMenu(menu, "Player Actions", admin =>
{
return CreatePlayerActionsMenu(admin);
});
_api.AddSubMenu(menu, "Server Settings", admin =>
{
return CreateServerSettingsMenu(admin);
}, false, "@css/root");
return menu;
Opening Menus
OpenMenu
Display a menu to a player.
void OpenMenu(object menu, CCSPlayerController player)
Example:
var menu = CreateMyMenu(player);
_api!.OpenMenu(menu, player);
Note: Usually menus open automatically when selected, but this can be used for direct opening.
Complete Examples
Simple Player Selection Menu
private void RegisterMenus()
{
_api!.RegisterMenuCategory("actions", "Player Actions", "@css/generic");
_api.RegisterMenu(
"actions",
"slay",
"Slay Player",
CreateSlayMenu,
"@css/slay"
);
}
private object CreateSlayMenu(CCSPlayerController admin, MenuContext context)
{
return _api!.CreateMenuWithPlayers(
context,
admin,
player => player.PawnIsAlive && admin.CanTarget(player),
(admin, target) =>
{
target.PlayerPawn?.Value?.CommitSuicide(false, true);
admin.PrintToChat($"Slayed {target.PlayerName}");
}
);
}
Nested Menu with Value Selection
private object CreateSetHpMenu(CCSPlayerController admin, MenuContext context)
{
var menu = _api!.CreateMenuWithBack(context, admin);
var players = _api.GetValidPlayers()
.Where(p => p.PawnIsAlive && admin.CanTarget(p));
foreach (var player in players)
{
_api.AddSubMenu(menu, player.PlayerName, admin =>
{
return CreateHpValueMenu(admin, player);
});
}
return menu;
}
private object CreateHpValueMenu(CCSPlayerController admin, CCSPlayerController target)
{
var menu = _api!.CreateMenuWithBack($"Set HP: {target.PlayerName}", "mycategory", admin);
var hpValues = new[] { 1, 10, 50, 100, 200, 500 };
foreach (var hp in hpValues)
{
_api.AddMenuOption(menu, $"{hp} HP", _ =>
{
if (target.IsValid && target.PawnIsAlive)
{
target.PlayerPawn?.Value?.SetHealth(hp);
admin.PrintToChat($"Set {target.PlayerName} HP to {hp}");
}
});
}
return menu;
}
Menu with Permissions
private object CreateAdminMenu(CCSPlayerController admin, MenuContext context)
{
var menu = _api!.CreateMenuWithBack(context, admin);
// Everyone with menu access sees this
_api.AddMenuOption(menu, "Basic Action", _ => DoBasicAction());
// Only root admins see this
_api.AddMenuOption(menu, "Dangerous Action", _ =>
{
DoDangerousAction();
}, false, "@css/root");
// Submenu with permission
_api.AddSubMenu(menu, "Advanced Options", admin =>
{
return CreateAdvancedMenu(admin);
}, false, "@css/root");
return menu;
}
Dynamic Menu with Current State
private object CreateToggleMenu(CCSPlayerController admin, MenuContext context)
{
var menu = _api!.CreateMenuWithBack(context, admin);
var players = _api.GetValidPlayers()
.Where(p => admin.CanTarget(p));
foreach (var player in players)
{
// Show current state in option name
bool hasGod = GodPlayers.Contains(player.Slot);
string status = hasGod ? "✓ ON" : "✗ OFF";
_api.AddMenuOption(menu, $"{player.PlayerName} ({status})", _ =>
{
if (hasGod)
GodPlayers.Remove(player.Slot);
else
GodPlayers.Add(player.Slot);
// Recreate menu to show updated state
var newMenu = CreateToggleMenu(admin, context);
_api.OpenMenu(newMenu, admin);
});
}
return menu;
}
Best Practices
1. Use MenuContext
// ✅ Good - Uses context
_api.RegisterMenu("cat", "id", "Title", CreateMenu, "@css/generic");
private object CreateMenu(CCSPlayerController admin, MenuContext context)
{
return _api.CreateMenuWithPlayers(context, admin, filter, action);
}
// ❌ Bad - Duplicates title and category
_api.RegisterMenu("cat", "id", "Title", CreateMenuOld, "@css/generic");
private object CreateMenuOld(CCSPlayerController admin)
{
return _api.CreateMenuWithPlayers("Title", "cat", admin, filter, action);
}
2. Register in OnSimpleAdminReady
_api.OnSimpleAdminReady += RegisterMenus;
RegisterMenus(); // Also call directly for hot reload
private void RegisterMenus()
{
if (_menusRegistered) return;
_api!.RegisterMenuCategory("category", "Category Name");
_api.RegisterMenu("category", "menu", "Menu Name", CreateMenu);
_menusRegistered = true;
}
3. Always Unregister
public override void Unload(bool hotReload)
{
if (_api == null) return;
_api.UnregisterMenu("category", "menu");
_api.OnSimpleAdminReady -= RegisterMenus;
}
4. Validate Player State
private object CreateMenu(CCSPlayerController admin, MenuContext context)
{
return _api!.CreateMenuWithPlayers(
context,
admin,
player => player.IsValid && // Player exists
!player.IsBot && // Not a bot
player.PawnIsAlive && // Alive
admin.CanTarget(player), // Can be targeted
(admin, target) =>
{
// Extra validation before action
if (!target.IsValid || !target.PawnIsAlive)
return;
DoAction(admin, target);
}
);
}
5. Use Translations for Menu Names
_api.RegisterMenuCategory(
"mycategory",
Localizer?["category_name"] ?? "Default Name",
"@css/generic"
);
_api.RegisterMenu(
"mycategory",
"mymenu",
Localizer?["menu_name"] ?? "Default Menu",
CreateMenu
);
Permission Override
The commandName parameter allows server admins to override menu permissions via CounterStrikeSharp's admin system.
Example:
_api.RegisterMenu(
"fun",
"god",
"God Mode",
CreateGodMenu,
"@css/cheats", // Default permission
"css_god" // Command name for override
);
Admin config can override:
{
"css_god": ["@css/vip"]
}
Now VIPs will see the God Mode menu instead of requiring @css/cheats!
Common Patterns
Player List with Actions
private object CreatePlayerListMenu(CCSPlayerController admin, MenuContext context)
{
var menu = _api!.CreateMenuWithBack(context, admin);
foreach (var player in _api.GetValidPlayers())
{
if (!admin.CanTarget(player)) continue;
_api.AddSubMenu(menu, player.PlayerName, admin =>
{
var actionMenu = _api.CreateMenuWithBack($"Actions: {player.PlayerName}", context.CategoryId, admin);
_api.AddMenuOption(actionMenu, "Slay", _ => player.CommitSuicide());
_api.AddMenuOption(actionMenu, "Kick", _ => KickPlayer(player));
_api.AddMenuOption(actionMenu, "Ban", _ => BanPlayer(admin, player));
return actionMenu;
});
}
return menu;
}
Category-Based Organization
private void RegisterAllMenus()
{
// Player management category
_api!.RegisterMenuCategory("players", "Player Management", "@css/generic");
_api.RegisterMenu("players", "kick", "Kick Player", CreateKickMenu, "@css/kick");
_api.RegisterMenu("players", "ban", "Ban Player", CreateBanMenu, "@css/ban");
// Server management category
_api.RegisterMenuCategory("server", "Server Management", "@css/generic");
_api.RegisterMenu("server", "map", "Change Map", CreateMapMenu, "@css/changemap");
_api.RegisterMenu("server", "settings", "Settings", CreateSettingsMenu, "@css/root");
}
Related APIs
- Commands API - Command integration
- Penalties API - Issue penalties from menus
- Utilities API - Helper functions for menus