From upstream 530f603: - css_unban updates the ban cache at once (and clears the engine banid with UnlockedCommands) instead of waiting up to a minute for the cache refresh. - css_prename is saved in sa_renames (migration 017) and re-applied on connect and name change, so it survives restarts. css_rename from a hidden admin renames again. - New css_history command and Players > History menu entry. Commands missing from an existing Commands.json are registered under their default name. - Migrations are copied with a glob, so 017 reaches the build output. Upstream's admin-loading changes, css_say change and css_penalties fix are left out.
749 lines
34 KiB
C#
749 lines
34 KiB
C#
using CounterStrikeSharp.API;
|
||
using CounterStrikeSharp.API.Core;
|
||
using CounterStrikeSharp.API.Core.Translations;
|
||
using CounterStrikeSharp.API.Modules.Admin;
|
||
using CounterStrikeSharp.API.Modules.Entities;
|
||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||
using CounterStrikeSharp.API.Modules.Utils;
|
||
using CS2_SimpleAdminApi;
|
||
|
||
namespace CS2_SimpleAdmin.Menus;
|
||
|
||
public abstract class BasicMenu
|
||
{
|
||
/// <summary>
|
||
/// Initializes all menus in the system by registering them with the MenuManager.
|
||
/// Register with translation keys instead of static names - translation happens per-player.
|
||
/// </summary>
|
||
public static void Initialize()
|
||
{
|
||
var manager = MenuManager.Instance;
|
||
|
||
// Players category menus - using translation keys
|
||
// zio: each entry names its command, so admin_overrides.json applies to the menu too.
|
||
manager.RegisterMenu("players", "slap", "sa_slap", CreateSlapMenu, "@css/slay", "css_slap");
|
||
manager.RegisterMenu("players", "slay", "sa_slay", CreateSlayMenu, "@css/slay", "css_slay");
|
||
manager.RegisterMenu("players", "kick", "sa_kick", CreateKickMenu, "@css/kick", "css_kick");
|
||
manager.RegisterMenu("players", "warn", "sa_warn", CreateWarnMenu, "@css/kick", "css_warn");
|
||
manager.RegisterMenu("players", "ban", "sa_ban", CreateBanMenu, "@css/ban", "css_ban");
|
||
manager.RegisterMenu("players", "gag", "sa_gag", CreateGagMenu, "@css/chat", "css_gag");
|
||
manager.RegisterMenu("players", "mute", "sa_mute", CreateMuteMenu, "@css/chat", "css_mute");
|
||
manager.RegisterMenu("players", "silence", "sa_silence", CreateSilenceMenu, "@css/chat", "css_silence");
|
||
manager.RegisterMenu("players", "team", "sa_team_force", CreateForceTeamMenu, "@css/kick", "css_team");
|
||
manager.RegisterMenu("players", "history", "sa_history", CreateHistoryMenu, "@css/kick", "css_history");
|
||
|
||
// Server category menus - using translation keys
|
||
manager.RegisterMenu("server", "plugins", "sa_menu_pluginsmanager_title", CreatePluginsMenu, "@css/root");
|
||
manager.RegisterMenu("server", "changemap", "sa_changemap", CreateChangeMapMenu, "@css/changemap", "css_map");
|
||
manager.RegisterMenu("server", "restart", "sa_restart_game", CreateRestartGameMenu, "@css/generic", "css_rr");
|
||
manager.RegisterMenu("server", "custom", "sa_menu_custom_commands", CreateCustomCommandsMenu, "@css/generic");
|
||
|
||
// Admin category menus - using translation keys
|
||
manager.RegisterMenu("admin", "add", "sa_admin_add", CreateAddAdminMenu, "@css/root");
|
||
manager.RegisterMenu("admin", "remove", "sa_admin_remove", CreateRemoveAdminMenu, "@css/root");
|
||
manager.RegisterMenu("admin", "reload", "sa_admin_reload", CreateReloadAdminsMenu, "@css/root");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for slapping players with selectable damage amounts.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the slap menu.</returns>
|
||
|
||
private static MenuBuilder CreateSlapMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var slapMenu = new MenuBuilder("sa_slap", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(admin.CanTarget);
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
slapMenu.AddSubMenu(playerName, () => CreateSlapDamageMenu(admin, capturedPlayer));
|
||
}
|
||
|
||
return slapMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates damage selection submenu for slapping a specific player.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player executing the slap.</param>
|
||
/// <param name="target">The target player to be slapped.</param>
|
||
/// <returns>A MenuBuilder instance for the slap damage menu.</returns>
|
||
private static MenuBuilder CreateSlapDamageMenu(CCSPlayerController admin, CCSPlayerController target)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
string localizedTitle;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
localizedTitle = $"{localizer?["sa_slap"] ?? "Slap"}: {target.PlayerName}";
|
||
}
|
||
|
||
var slapDamageMenu = new MenuBuilder(localizedTitle);
|
||
var damages = new[] { 0, 1, 5, 10, 50, 100 };
|
||
|
||
foreach (var damage in damages)
|
||
{
|
||
slapDamageMenu.AddOption($"{damage} HP", currentAdmin =>
|
||
{
|
||
if (target.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Slap(currentAdmin, target, damage);
|
||
// Reopen the same menu (not create new one) to keep back button working
|
||
slapDamageMenu.OpenMenu(currentAdmin);
|
||
}
|
||
});
|
||
}
|
||
|
||
return slapDamageMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for slaying (killing) players.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the slay menu.</returns>
|
||
private static MenuBuilder CreateSlayMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var slayMenu = new MenuBuilder("sa_slay", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(admin.CanTarget);
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
slayMenu.AddOption(playerName, _ =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Slay(admin, capturedPlayer);
|
||
}
|
||
});
|
||
}
|
||
|
||
return slayMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates the penalty history menu: pick an online player, then one entry per past penalty.
|
||
/// Selecting an entry prints its full details to the admin's console.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the history menu.</returns>
|
||
private static MenuBuilder CreateHistoryMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var historyMenu = new MenuBuilder("sa_history", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
|
||
historyMenu.AddOption(playerName, _ =>
|
||
{
|
||
var steamId = player.SteamID;
|
||
var name = player.PlayerName;
|
||
var adminSteamId = admin.SteamID;
|
||
|
||
// The list is built after the DB round trip, so it is opened from the world update rather than returned here
|
||
Task.Run(async () =>
|
||
{
|
||
var rows = await CS2_SimpleAdmin.PlayerManager.GetPenaltyHistory(steamId);
|
||
await Server.NextWorldUpdateAsync(() =>
|
||
{
|
||
// The slot may have been reused by another player while the query ran
|
||
if (!admin.IsValid || admin.SteamID != adminSteamId) return;
|
||
|
||
if (rows.Count == 0)
|
||
{
|
||
CS2_SimpleAdmin.Instance.PrintHistory(admin.PrintToConsole, name, steamId, rows);
|
||
return;
|
||
}
|
||
|
||
var list = new MenuBuilder("sa_history", admin, localizer);
|
||
foreach (var row in rows)
|
||
{
|
||
var active = (string)row.status == "ACTIVE";
|
||
var label = $"[{(active ? $"{ChatColors.LightRed}X" : $"{ChatColors.Lime}✔️")}{ChatColors.Default}] {(string)row.type} {(string)row.reason}";
|
||
list.AddOption(label.Length > 40 ? label[..40] : label,
|
||
_ => CS2_SimpleAdmin.Instance.PrintHistory(admin.PrintToConsole, name, steamId, [row]));
|
||
}
|
||
|
||
// Built standalone, so the back button must be wired to the player list explicitly
|
||
list.WithBackAction(historyMenu.OpenMenu).OpenMenu(admin);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
return historyMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for kicking players with reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the kick menu.</returns>
|
||
private static MenuBuilder CreateKickMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var kickMenu = new MenuBuilder("sa_kick", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
kickMenu.AddSubMenu(playerName, () => CreateReasonMenu(admin, capturedPlayer, "Kick", PenaltyType.Kick,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Kick(admin, capturedPlayer, reason, admin.PlayerName);
|
||
}
|
||
}));
|
||
}
|
||
|
||
return kickMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for warning players with duration and reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the warn menu.</returns>
|
||
private static MenuBuilder CreateWarnMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var warnMenu = new MenuBuilder("sa_warn", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
warnMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, capturedPlayer, "Warn",
|
||
(_, _, duration) => CreateReasonMenu(admin, capturedPlayer, "Warn", PenaltyType.Warn,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Warn(admin, capturedPlayer, duration, reason, admin.PlayerName);
|
||
}
|
||
})));
|
||
}
|
||
|
||
return warnMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for banning players with duration and reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the ban menu.</returns>
|
||
private static MenuBuilder CreateBanMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var banMenu = new MenuBuilder("sa_ban", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
banMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, capturedPlayer, "Ban",
|
||
(_, _, duration) => CreateReasonMenu(admin, capturedPlayer, "Ban", PenaltyType.Ban,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Ban(admin, capturedPlayer, duration, reason, admin.PlayerName);
|
||
}
|
||
})));
|
||
}
|
||
|
||
return banMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for gagging (text chat muting) players with duration and reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the gag menu.</returns>
|
||
private static MenuBuilder CreateGagMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var gagMenu = new MenuBuilder("sa_gag", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
gagMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, capturedPlayer, "Gag",
|
||
(_, _, duration) => CreateReasonMenu(admin, capturedPlayer, "Gag", PenaltyType.Gag,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Gag(admin, capturedPlayer, duration, reason);
|
||
}
|
||
})));
|
||
}
|
||
|
||
return gagMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for muting (voice chat muting) players with duration and reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the mute menu.</returns>
|
||
private static MenuBuilder CreateMuteMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var muteMenu = new MenuBuilder("sa_mute", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
muteMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, capturedPlayer, "Mute",
|
||
(_, _, duration) => CreateReasonMenu(admin, capturedPlayer, "Mute", PenaltyType.Mute,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Mute(admin, capturedPlayer, duration, reason);
|
||
}
|
||
})));
|
||
}
|
||
|
||
return muteMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for silencing (both text and voice chat muting) players with duration and reason selection.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the silence menu.</returns>
|
||
private static MenuBuilder CreateSilenceMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var silenceMenu = new MenuBuilder("sa_silence", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
silenceMenu.AddSubMenu(playerName, () => CreateDurationMenu(admin, capturedPlayer, "Silence",
|
||
(_, _, duration) => CreateReasonMenu(admin, capturedPlayer, "Silence", PenaltyType.Silence,
|
||
(_, _, reason) =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.Silence(admin, capturedPlayer, duration, reason);
|
||
}
|
||
})));
|
||
}
|
||
|
||
return silenceMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for forcing players to switch teams.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the force team menu.</returns>
|
||
private static MenuBuilder CreateForceTeamMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var teamMenu = new MenuBuilder("sa_team_force", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
teamMenu.AddSubMenu(playerName, () => CreateTeamSelectionMenu(admin, capturedPlayer));
|
||
}
|
||
|
||
return teamMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates team selection submenu for forcing a specific player to a team.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player executing the team change.</param>
|
||
/// <param name="target">The target player to be moved.</param>
|
||
/// <returns>A MenuBuilder instance for the team selection menu.</returns>
|
||
private static MenuBuilder CreateTeamSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
|
||
// Localize title for admin's language
|
||
string localizedTitle;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
localizedTitle = $"{localizer?["sa_team_force"] ?? "Force Team"}: {target.PlayerName}";
|
||
}
|
||
|
||
var teamSelectionMenu = new MenuBuilder(localizedTitle);
|
||
|
||
// Localize team options for admin's language
|
||
string ctName, tName, swapName, specName;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
ctName = localizer?["sa_team_ct"] ?? "CT";
|
||
tName = localizer?["sa_team_t"] ?? "T";
|
||
swapName = localizer?["sa_team_swap"] ?? "Swap";
|
||
specName = localizer?["sa_team_spec"] ?? "Spec";
|
||
}
|
||
|
||
var teams = new[]
|
||
{
|
||
(ctName, "ct", CsTeam.CounterTerrorist),
|
||
(tName, "t", CsTeam.Terrorist),
|
||
(swapName, "swap", CsTeam.Spectator),
|
||
(specName, "spec", CsTeam.Spectator)
|
||
};
|
||
|
||
foreach (var (name, teamName, teamNum) in teams)
|
||
{
|
||
teamSelectionMenu.AddOption(name, _ =>
|
||
{
|
||
if (target.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.ChangeTeam(admin, target, teamName, teamNum, true);
|
||
}
|
||
});
|
||
}
|
||
|
||
return teamSelectionMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for managing server plugins.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the plugins menu.</returns>
|
||
private static MenuBuilder CreatePluginsMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var pluginsMenu = new MenuBuilder("sa_menu_pluginsmanager_title", admin, localizer);
|
||
|
||
pluginsMenu.AddOption("Open Plugins Manager", _ =>
|
||
{
|
||
admin.ExecuteClientCommandFromServer("css_pluginsmanager");
|
||
});
|
||
|
||
return pluginsMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for changing the current map (includes default and workshop maps).
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the change map menu.</returns>
|
||
private static MenuBuilder CreateChangeMapMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var mapMenu = new MenuBuilder("sa_changemap", admin, localizer);
|
||
|
||
// Add default maps
|
||
var maps = CS2_SimpleAdmin.Instance.Config.DefaultMaps;
|
||
foreach (var map in maps)
|
||
{
|
||
mapMenu.AddOption(map, _ =>
|
||
{
|
||
CS2_SimpleAdmin.Instance.ChangeMap(admin, map);
|
||
});
|
||
}
|
||
|
||
// Add workshop maps
|
||
var wsMaps = CS2_SimpleAdmin.Instance.Config.WorkshopMaps;
|
||
foreach (var wsMap in wsMaps)
|
||
{
|
||
mapMenu.AddOption($"{wsMap.Key} (WS)", _ =>
|
||
{
|
||
CS2_SimpleAdmin.Instance.ChangeWorkshopMap(admin, wsMap.Value?.ToString() ?? wsMap.Key);
|
||
});
|
||
}
|
||
|
||
return mapMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for restarting the current game/round.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the restart game menu.</returns>
|
||
private static MenuBuilder CreateRestartGameMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var restartMenu = new MenuBuilder("sa_restart_game", admin, localizer);
|
||
|
||
restartMenu.AddOption("Restart Round", _ =>
|
||
{
|
||
CS2_SimpleAdmin.RestartGame(admin);
|
||
});
|
||
|
||
return restartMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for executing custom server commands defined in configuration.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the custom commands menu.</returns>
|
||
private static MenuBuilder CreateCustomCommandsMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var customMenu = new MenuBuilder("sa_menu_custom_commands", admin, localizer);
|
||
|
||
var customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands;
|
||
|
||
foreach (var customCommand in customCommands)
|
||
{
|
||
if (string.IsNullOrEmpty(customCommand.DisplayName) || string.IsNullOrEmpty(customCommand.Command))
|
||
continue;
|
||
|
||
var steamId = new SteamID(admin.SteamID);
|
||
if (!AdminManager.PlayerHasPermissions(steamId, customCommand.Flag))
|
||
continue;
|
||
|
||
customMenu.AddOption(customCommand.DisplayName, _ =>
|
||
{
|
||
Helper.TryLogCommandOnDiscord(admin, customCommand.Command);
|
||
|
||
if (customCommand.ExecuteOnClient)
|
||
admin.ExecuteClientCommandFromServer(customCommand.Command);
|
||
else
|
||
Server.ExecuteCommand(customCommand.Command);
|
||
});
|
||
}
|
||
|
||
return customMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for adding admin privileges to players.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the add admin menu.</returns>
|
||
private static MenuBuilder CreateAddAdminMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var addAdminMenu = new MenuBuilder("sa_admin_add", admin, localizer);
|
||
|
||
var players = Helper.GetValidPlayers().Where(p => !p.IsBot && admin.CanTarget(p));
|
||
|
||
foreach (var player in players)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
addAdminMenu.AddSubMenu(playerName, () => CreateAdminFlagsMenu(admin, capturedPlayer));
|
||
}
|
||
|
||
return addAdminMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates admin flags selection submenu for granting specific permissions to a player.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player granting permissions.</param>
|
||
/// <param name="target">The target player to receive admin privileges.</param>
|
||
/// <returns>A MenuBuilder instance for the admin flags menu.</returns>
|
||
private static MenuBuilder CreateAdminFlagsMenu(CCSPlayerController admin, CCSPlayerController target)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
|
||
// Localize title for admin's language
|
||
string localizedTitle;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
localizedTitle = $"{localizer?["sa_admin_add"] ?? "Add Admin"}: {target.PlayerName}";
|
||
}
|
||
|
||
var flagsMenu = new MenuBuilder(localizedTitle);
|
||
|
||
foreach (var adminFlag in CS2_SimpleAdmin.Instance.Config.MenuConfigs.AdminFlags)
|
||
{
|
||
var hasFlag = AdminManager.PlayerHasPermissions(target, adminFlag.Flag);
|
||
flagsMenu.AddOption(adminFlag.Name, _ =>
|
||
{
|
||
if (target.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.AddAdmin(admin, target.SteamID.ToString(), target.PlayerName, adminFlag.Flag, 10);
|
||
}
|
||
}, hasFlag); // Disabled if player already has this flag
|
||
}
|
||
|
||
return flagsMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for removing admin privileges from players.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the remove admin menu.</returns>
|
||
private static MenuBuilder CreateRemoveAdminMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var removeAdminMenu = new MenuBuilder("sa_admin_remove", admin, localizer);
|
||
|
||
var adminPlayers = Helper.GetValidPlayers().Where(p =>
|
||
AdminManager.GetPlayerAdminData(p)?.Flags.Count > 0 &&
|
||
p != admin &&
|
||
admin.CanTarget(p));
|
||
|
||
foreach (var player in adminPlayers)
|
||
{
|
||
var capturedPlayer = player; // Capture to local variable to avoid closure issue
|
||
var playerName = capturedPlayer.PlayerName.Length > 26 ? capturedPlayer.PlayerName[..26] : capturedPlayer.PlayerName;
|
||
removeAdminMenu.AddOption(playerName, _ =>
|
||
{
|
||
if (capturedPlayer.IsValid)
|
||
{
|
||
CS2_SimpleAdmin.Instance.RemoveAdmin(admin, capturedPlayer.SteamID.ToString());
|
||
}
|
||
});
|
||
}
|
||
|
||
return removeAdminMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates menu for reloading admin list from database.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player opening the menu.</param>
|
||
/// <returns>A MenuBuilder instance for the reload admins menu.</returns>
|
||
private static MenuBuilder CreateReloadAdminsMenu(CCSPlayerController admin)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
var reloadMenu = new MenuBuilder("sa_admin_reload", admin, localizer);
|
||
|
||
reloadMenu.AddOption("Reload Admins", _ =>
|
||
{
|
||
CS2_SimpleAdmin.Instance.ReloadAdmins(admin);
|
||
});
|
||
|
||
return reloadMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates duration selection submenu for time-based penalties (ban, mute, gag, etc.).
|
||
/// </summary>
|
||
/// <param name="admin">The admin player selecting duration.</param>
|
||
/// <param name="player">The target player for the penalty.</param>
|
||
/// <param name="actionName">The name of the penalty action (e.g., "Kick", "Ban").</param>
|
||
/// <param name="onSelectAction">Callback function that returns the next menu when duration is selected.</param>
|
||
/// <returns>A MenuBuilder instance for the duration menu.</returns>
|
||
private static MenuBuilder CreateDurationMenu(CCSPlayerController admin, CCSPlayerController player, string actionName,
|
||
Func<CCSPlayerController, CCSPlayerController, int, MenuBuilder> onSelectAction)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
|
||
// Convert action name to translation key (e.g., "Ban" -> "sa_ban")
|
||
var actionKey = actionName.ToLower() switch
|
||
{
|
||
"kick" => "sa_kick",
|
||
"ban" => "sa_ban",
|
||
"warn" => "sa_warn",
|
||
"gag" => "sa_gag",
|
||
"mute" => "sa_mute",
|
||
"silence" => "sa_silence",
|
||
_ => actionName
|
||
};
|
||
|
||
// Localize title for admin's language
|
||
string localizedAction, durationText;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
localizedAction = localizer?[actionKey] ?? actionName;
|
||
durationText = localizer?["sa_duration"] ?? "Duration";
|
||
}
|
||
|
||
var durationMenu = new MenuBuilder($"{localizedAction} {durationText}: {player.PlayerName}");
|
||
|
||
foreach (var durationItem in CS2_SimpleAdmin.Instance.Config.MenuConfigs.Durations)
|
||
{
|
||
var capturedDuration = durationItem.Duration; // Capture to avoid closure issue
|
||
durationMenu.AddSubMenu(durationItem.Name, () => onSelectAction(admin, player, capturedDuration));
|
||
}
|
||
|
||
return durationMenu.WithBackButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates reason selection submenu for penalties with predefined reasons from configuration.
|
||
/// </summary>
|
||
/// <param name="admin">The admin player selecting reason.</param>
|
||
/// <param name="player">The target player for the penalty.</param>
|
||
/// <param name="actionName">The name of the penalty action (e.g., "Kick", "Ban").</param>
|
||
/// <param name="penaltyType">The type of penalty to determine which reason list to use.</param>
|
||
/// <param name="onSelectAction">Callback action executed when reason is selected.</param>
|
||
/// <returns>A MenuBuilder instance for the reason menu.</returns>
|
||
private static MenuBuilder CreateReasonMenu(CCSPlayerController admin, CCSPlayerController player, string actionName,
|
||
PenaltyType penaltyType, Action<CCSPlayerController, CCSPlayerController, string> onSelectAction)
|
||
{
|
||
var localizer = CS2_SimpleAdmin._localizer;
|
||
|
||
// Convert action name to translation key
|
||
var actionKey = actionName.ToLower() switch
|
||
{
|
||
"kick" => "sa_kick",
|
||
"ban" => "sa_ban",
|
||
"warn" => "sa_warn",
|
||
"gag" => "sa_gag",
|
||
"mute" => "sa_mute",
|
||
"silence" => "sa_silence",
|
||
_ => actionName
|
||
};
|
||
|
||
// Localize title for admin's language
|
||
string localizedAction, reasonText;
|
||
using (new WithTemporaryCulture(admin.GetLanguage()))
|
||
{
|
||
localizedAction = localizer?[actionKey] ?? actionName;
|
||
reasonText = localizer?["sa_reason"] ?? "Reason";
|
||
}
|
||
|
||
var reasonMenu = new MenuBuilder($"{localizedAction} {reasonText}: {player.PlayerName}");
|
||
|
||
var reasons = penaltyType switch
|
||
{
|
||
PenaltyType.Ban => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons,
|
||
PenaltyType.Kick => CS2_SimpleAdmin.Instance.Config.MenuConfigs.KickReasons,
|
||
PenaltyType.Mute => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||
PenaltyType.Warn => CS2_SimpleAdmin.Instance.Config.MenuConfigs.WarnReasons,
|
||
PenaltyType.Gag or PenaltyType.Silence => CS2_SimpleAdmin.Instance.Config.MenuConfigs.MuteReasons,
|
||
_ => CS2_SimpleAdmin.Instance.Config.MenuConfigs.BanReasons
|
||
};
|
||
|
||
foreach (var reason in reasons)
|
||
{
|
||
reasonMenu.AddOption(reason, _ =>
|
||
{
|
||
onSelectAction(admin, player, reason);
|
||
});
|
||
}
|
||
|
||
return reasonMenu.WithBackButton();
|
||
}
|
||
}
|