Menüs-API

Complete reference for creating interactive admin menus.

RegisterMenuCategory

Register a new menu category in the admin menu.

void RegisterMenuCategory(string categoryId, string categoryName, string permission = "@css/generic")

Parameters:

Beispiel:

_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");
};

---

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:

Beispiel:

_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;
}

---

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:

Beispiel:

// ✅ 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)

Beispiel:

public override void Unload(bool hotReload)
{
    _api?.UnregisterMenu("mycategory", "mymenu");
}

---

CreateMenuWithBack

Create a menu with automatic back button.

object CreateMenuWithBack(string title, string categoryId, CCSPlayerController player)

Parameters:

Beispiel:

var menu = _api!.CreateMenuWithBack("My Menu", "mycategory", admin);
_api.AddMenuOption(menu, "Option 1", _ => DoAction1());
_api.AddMenuOption(menu, "Option 2", _ => DoAction2());
return menu;

---

Create a menu using context - no duplication!

object CreateMenuWithBack(MenuContext context, CCSPlayerController player)

Beispiel:

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:

Beispiel:

return _api!.CreateMenuWithPlayers(
    "Select Player",
    "mycategory",
    admin,
    player => player.IsValid && admin.CanTarget(player),
    (admin, target) =>
    {
        // Do something with selected player
        DoAction(admin, target);
    }
);

---

object CreateMenuWithPlayers(
    MenuContext context,
    CCSPlayerController admin,
    Func<CCSPlayerController, bool> filter,
    Action<CCSPlayerController, CCSPlayerController> onSelect
)

Beispiel:

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)
    );
}

---

AddMenuOption

Add a clickable option to a menu.

void AddMenuOption(
    object menu,
    string name,
    Action<CCSPlayerController> action,
    bool disabled = false,
    string? permission = null
)

Parameters:

Beispiel:

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:

Beispiel:

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 Menüs

OpenMenu

Display a menu to a player.

void OpenMenu(object menu, CCSPlayerController player)

Beispiel:

var menu = CreateMyMenu(player);
_api!.OpenMenu(menu, player);

Note: Usually menus open automatically when selected, but this can be used for direct opening.

---

Vollständige Beispiele

Simple Spieler Selection Menü

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 Menü 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;
}

---

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 Menü 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;
}

---

Bewährte Vorgehensweisen

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 Spieler 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 Übersetzungen for Menü Names

_api.RegisterMenuCategory(
    "mycategory",
    Localizer?["category_name"] ?? "Default Name",
    "@css/generic"
);

_api.RegisterMenu(
    "mycategory",
    "mymenu",
    Localizer?["menu_name"] ?? "Default Menu",
    CreateMenu
);

---

Berechtigung Override

The commandName parameter allows server admins to override menu permissions via CounterStrikeSharp's admin system.

Beispiel:

_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!

---

Häufige Muster

Spieler 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");
}

---