Migrate menus and votes to Panorama UI Kit

Replace MenuManagerCS2 with Panorama UI Kit for the admin menus and
css_vote. Layout names (menu, vote, banner, toast) are configurable
under MenuConfig and preloaded on startup; MenuType is removed and
ConfigVersion bumped to 26.

- Drop MenuManagerApi.dll and the old chat menu classes
- Reference PanoramaUiKit.Shared.dll, bump CounterStrikeSharp to 1.0.376
- Add .editorconfig and CODING_CONVENTIONS.md, apply them codebase-wide
- Remove the CS2-SimpleAdmin-docs site
- Rewrite README for the fork
This commit is contained in:
Salvatore 2026-10-21 18:51:18 +02:00
parent 03a454b1b5
commit 460d8b2b85
112 changed files with 5559 additions and 36371 deletions

View file

@ -1,24 +1,25 @@
using System.Numerics;
using System.Text;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Core.Translations;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.UserMessages;
using CounterStrikeSharp.API.Modules.Utils;
using CS2_SimpleAdmin.Managers;
using CS2_SimpleAdmin.Models;
using CS2_SimpleAdminApi;
using Microsoft.Extensions.Logging;
using System.Text;
using CounterStrikeSharp.API.Core.Translations;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.UserMessages;
namespace CS2_SimpleAdmin;
public partial class CS2_SimpleAdmin
{
private bool _serverLoading;
private void RegisterEvents()
{
RegisterListener<Listeners.OnMapStart>(OnMapStart);
@ -27,14 +28,16 @@ public partial class CS2_SimpleAdmin
RegisterListener<Listeners.OnClientConnected>(OnClientConnected);
RegisterListener<Listeners.OnGameServerSteamAPIActivated>(OnGameServerSteamAPIActivated);
if (Config.OtherSettings.UserMessageGagChatType)
{
HookUserMessage(118, HookUmChat);
}
AddCommandListener(null, ComamndListenerHandler);
// AddCommandListener("callvote", OnCommandCallVote);
// AddCommandListener("say", OnCommandSay);
// AddCommandListener("say_team", OnCommandTeamSay);
}
private void UnregisterEvents()
{
RemoveListener<Listeners.OnMapStart>(OnMapStart);
@ -42,15 +45,17 @@ public partial class CS2_SimpleAdmin
RemoveListener<Listeners.OnClientConnected>(OnClientConnected);
RemoveListener<Listeners.OnGameServerSteamAPIActivated>(OnGameServerSteamAPIActivated);
if (Config.OtherSettings.UserMessageGagChatType)
{
UnhookUserMessage(118, HookUmChat);
}
RemoveCommandListener(null!, ComamndListenerHandler, HookMode.Pre);
// AddCommandListener("callvote", OnCommandCallVote);
// AddCommandListener("say", OnCommandSay);
// AddCommandListener("say_team", OnCommandTeamSay);
}
// private HookResult OnCommandCallVote(CCSPlayerController? caller, CommandInfo info)
// {
// var voteType = info.GetArg(1).ToLower();
@ -71,8 +76,10 @@ public partial class CS2_SimpleAdmin
private void OnGameServerSteamAPIActivated()
{
if (ServerLoaded || _serverLoading)
{
return;
}
_serverLoading = true;
new ServerManager().LoadServerData();
}
@ -81,17 +88,21 @@ public partial class CS2_SimpleAdmin
public HookResult OnClientDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
{
if (@event.Reason is 149 or 6)
{
info.DontBroadcast = true;
}
var player = @event.Userid;
CCSPlayerController? player = @event.Userid;
#if DEBUG
Logger.LogCritical("[OnClientDisconnect] Before");
#endif
if (player == null || !player.IsValid || player.IsHLTV)
{
return HookResult.Continue;
}
CachedPlayers.Remove(player);
BotPlayers.Remove(player);
SilentPlayers.Remove(player.Slot);
@ -107,10 +118,12 @@ public partial class CS2_SimpleAdmin
try
{
if (DisconnectedPlayers.Count >= Config.OtherSettings.DisconnectedPlayersHistoryCount)
{
DisconnectedPlayers.RemoveAt(0);
}
var steamId = new SteamID(player.SteamID);
var disconnectedPlayer = DisconnectedPlayers.FirstOrDefault(p => p.SteamId == steamId);
SteamID steamId = new SteamID(player.SteamID);
DisconnectedPlayer? disconnectedPlayer = DisconnectedPlayers.FirstOrDefault(p => p.SteamId == steamId);
if (disconnectedPlayer != null)
{
@ -123,13 +136,15 @@ public partial class CS2_SimpleAdmin
DisconnectedPlayers.Add(new DisconnectedPlayer(steamId, player.PlayerName,
player.IpAddress?.Split(":")[0], Time.ActualDateTime()));
}
PlayerPenaltyManager.RemoveAllPenalties(player.Slot);
if (player.UserId.HasValue)
PlayersInfo.TryRemove(player.SteamID, out _);
if (!PermissionManager.AdminCache.TryGetValue(steamId, out var data)
PlayerPenaltyManager.RemoveAllPenalties(player.Slot);
if (player.UserId.HasValue)
{
PlayersInfo.TryRemove(player.SteamID, out _);
}
if (!PermissionManager.AdminCache.TryGetValue(steamId, out (DateTime? ExpirationTime, List<string> Flags) data)
|| !(data.ExpirationTime <= Time.ActualDateTime()))
{
return HookResult.Continue;
@ -137,10 +152,12 @@ public partial class CS2_SimpleAdmin
AdminManager.RemovePlayerPermissions(steamId, PermissionManager.AdminCache[steamId].Flags.ToArray());
AdminManager.RemovePlayerFromGroup(steamId, true, PermissionManager.AdminCache[steamId].Flags.ToArray());
var adminData = AdminManager.GetPlayerAdminData(steamId);
AdminData? adminData = AdminManager.GetPlayerAdminData(steamId);
if (adminData == null || data.Flags.ToList().Count != 0 && adminData.Groups.ToList().Count != 0)
{
return HookResult.Continue;
}
AdminManager.ClearPlayerPermissions(steamId);
AdminManager.RemovePlayerAdminData(steamId);
@ -160,64 +177,70 @@ public partial class CS2_SimpleAdmin
Logger.LogCritical("[OnClientConnect]");
#endif
var player = Utilities.GetPlayerFromSlot(playerslot);
CCSPlayerController? player = Utilities.GetPlayerFromSlot(playerslot);
if (player == null || !player.IsValid || player.IsBot)
{
return;
}
PlayerManager.LoadPlayerData(player);
}
private void OnClientConnected(int playerslot)
{
#if DEBUG
Logger.LogCritical("[OnClientConnected]");
#endif
var player = Utilities.GetPlayerFromSlot(playerslot);
CCSPlayerController? player = Utilities.GetPlayerFromSlot(playerslot);
if (player == null || !player.IsValid || player.IsBot)
{
return;
}
if (!CachedPlayers.Contains(player))
{
CachedPlayers.Add(player);
}
PlayerManager.LoadPlayerData(player);
}
// private void OnClientConnect(int playerslot, string name, string ipaddress)
// {
// #if DEBUG
// Logger.LogCritical("[OnClientConnect]");
// #endif
// if (Config.OtherSettings.BanType == 0)
// return;
//
// if (Instance.CacheManager != null && !Instance.CacheManager.IsPlayerBanned(null, ipaddress.Split(":")[0]))
// return;
//
// var testPlayer = Utilities.GetPlayerFromSlot(playerslot);
// if (testPlayer == null)
// return;
// Logger.LogInformation($"Gracz {testPlayer.PlayerName} ({testPlayer.SteamID.ToString()}) Czas: {DateTime.Now}");
//
// Server.NextFrame((() =>
// {
// var player = Utilities.GetPlayerFromSlot(playerslot);
// if (player == null || !player.IsValid || player.IsBot)
// return;
//
// Helper.KickPlayer(player, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
// }));
//
// // Server.NextFrame(() =>
// // {
// // var player = Utilities.GetPlayerFromSlot(playerslot);
// //
// // if (player == null || !player.IsValid || player.IsBot)
// // return;
// //
// // new PlayerManager().LoadPlayerData(player);
// // });
// }
// private void OnClientConnect(int playerslot, string name, string ipaddress)
// {
// #if DEBUG
// Logger.LogCritical("[OnClientConnect]");
// #endif
// if (Config.OtherSettings.BanType == 0)
// return;
//
// if (Instance.CacheManager != null && !Instance.CacheManager.IsPlayerBanned(null, ipaddress.Split(":")[0]))
// return;
//
// var testPlayer = Utilities.GetPlayerFromSlot(playerslot);
// if (testPlayer == null)
// return;
// Logger.LogInformation($"Gracz {testPlayer.PlayerName} ({testPlayer.SteamID.ToString()}) Czas: {DateTime.Now}");
//
// Server.NextFrame((() =>
// {
// var player = Utilities.GetPlayerFromSlot(playerslot);
// if (player == null || !player.IsValid || player.IsBot)
// return;
//
// Helper.KickPlayer(player, NetworkDisconnectionReason.NETWORK_DISCONNECT_REJECT_BANNED);
// }));
//
// // Server.NextFrame(() =>
// // {
// // var player = Utilities.GetPlayerFromSlot(playerslot);
// //
// // if (player == null || !player.IsValid || player.IsBot)
// // return;
// //
// // new PlayerManager().LoadPlayerData(player);
// // });
// }
[GameEventHandler]
public HookResult OnPlayerFullConnect(EventPlayerConnectFull @event, GameEventInfo info)
@ -226,10 +249,12 @@ public partial class CS2_SimpleAdmin
Logger.LogCritical("[OnPlayerFullConnect]");
#endif
var player = @event.Userid;
CCSPlayerController? player = @event.Userid;
if (player == null || !player.IsValid)
{
return HookResult.Continue;
}
if (player is { IsBot: true, IsHLTV: false })
{
@ -248,22 +273,26 @@ public partial class CS2_SimpleAdmin
Logger.LogCritical("[OnRoundStart]");
#endif
foreach (var player in PlayersInfo.Values)
foreach (PlayerInfo player in PlayersInfo.Values)
{
player.DiePosition = null;
}
AddTimer(0.5f, () =>
{
foreach (var list in RenamedPlayers)
foreach (KeyValuePair<ulong, string> list in RenamedPlayers)
{
var player = Utilities.GetPlayerFromSteamId(list.Key);
CCSPlayerController? player = Utilities.GetPlayerFromSteamId(list.Key);
if (player == null || !player.IsValid || player.Connected != PlayerConnectedState.Connected)
{
continue;
}
if (player.PlayerName.Equals(list.Value))
{
continue;
}
player.Rename(list.Value);
}
@ -271,32 +300,41 @@ public partial class CS2_SimpleAdmin
return HookResult.Continue;
}
private HookResult HookUmChat(UserMessage um)
{
var author = Utilities.GetPlayerFromIndex(um.ReadInt("entityindex"));
CCSPlayerController? author = Utilities.GetPlayerFromIndex(um.ReadInt("entityindex"));
if (author == null || !author.IsValid || author.IsBot)
{
return HookResult.Continue;
}
if (!PlayerPenaltyManager.IsPenalized(author.Slot, PenaltyType.Gag, out DateTime? endDateTime) &&
!PlayerPenaltyManager.IsPenalized(author.Slot, PenaltyType.Silence, out endDateTime))
{
return HookResult.Continue;
if (_localizer == null || endDateTime == null)
return HookResult.Continue;
}
var message = um.ReadString("param2");
var triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
if (!triggers.Any(trigger => message.StartsWith(trigger))) return HookResult.Stop;
for (var i = um.Recipients.Count - 1; i >= 0; i--)
if (_localizer == null || endDateTime == null)
{
return HookResult.Continue;
}
string message = um.ReadString("param2");
IEnumerable<string> triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
if (!triggers.Any(trigger => message.StartsWith(trigger)))
{
return HookResult.Stop;
}
for (int i = um.Recipients.Count - 1; i >= 0; i--)
{
if (um.Recipients[i] != author)
{
um.Recipients.RemoveAt(i);
}
}
return HookResult.Continue;
// author.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", author.GetLanguage()));
@ -305,12 +343,16 @@ public partial class CS2_SimpleAdmin
private HookResult ComamndListenerHandler(CCSPlayerController? player, CommandInfo info)
{
if (player == null || !player.IsValid || player.IsBot)
{
return HookResult.Continue;
}
var command = info.GetArg(0).ToLower();
string command = info.GetArg(0).ToLower();
if (Config.OtherSettings.AdditionalCommandsToLog.Contains(command))
{
Helper.LogCommand(player, info);
}
switch (command)
{
@ -318,59 +360,85 @@ public partial class CS2_SimpleAdmin
AddTimer(1.0f, () => ReloadAdmins(null));
return HookResult.Continue;
case "callvote":
{
var voteType = info.GetArg(1).ToLower();
if (voteType != "kick")
return HookResult.Continue;
{
string voteType = info.GetArg(1).ToLower();
if (voteType != "kick")
{
return HookResult.Continue;
}
CCSPlayerController? target;
if (int.TryParse(info.GetArg(2), out int userId))
{
target = Utilities.GetPlayerFromUserid(userId);
}
else
{
target = null;
}
if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.Connected)
{
return HookResult.Continue;
}
if (!player.CanTarget(target))
{
return HookResult.Stop;
}
var target = int.TryParse(info.GetArg(2), out var userId)
? Utilities.GetPlayerFromUserid(userId)
: null;
if (target == null || !target.IsValid || target.Connected != PlayerConnectedState.Connected)
return HookResult.Continue;
return !player.CanTarget(target) ? HookResult.Stop : HookResult.Continue;
}
}
}
if (!command.Contains("say"))
{
return HookResult.Continue;
}
if (info.GetArg(1).Length == 0)
{
return HookResult.Stop;
var triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
}
IEnumerable<string> triggers = CoreConfig.PublicChatTrigger.Concat(CoreConfig.SilentChatTrigger);
if (triggers.Any(trigger => info.GetArg(1).StartsWith(trigger)))
{
return HookResult.Continue;
}
// if (!Config.OtherSettings.UserMessageGagChatType)
// {
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out DateTime? endDateTime) ||
PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out endDateTime))
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out DateTime? endDateTime) ||
PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out endDateTime))
{
if (_localizer != null && endDateTime is not null)
{
if (_localizer != null && endDateTime is not null)
player.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", player.GetLanguage()));
return HookResult.Stop;
player.SendLocalizedMessage(_localizer, "sa_player_penalty_chat_active", endDateTime.Value.ToString("g", player.GetLanguage()));
}
return HookResult.Stop;
}
// }
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat") && command == "say" && info.GetArg(1).StartsWith($"@"))
{
player.ExecuteClientCommandFromServer($"css_say {info.GetArg(1).Remove(0, 1)}");
return HookResult.Stop;
}
if (command != "say_team" || !info.GetArg(1).StartsWith($"@")) return HookResult.Continue;
if (command != "say_team" || !info.GetArg(1).StartsWith($"@"))
{
return HookResult.Continue;
}
StringBuilder sb = new();
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat"))
{
sb.Append(_localizer!["sa_adminchat_template_admin", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
foreach (var p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
foreach (CCSPlayerController? p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
{
p.PrintToChat(sb.ToString());
}
@ -379,7 +447,7 @@ public partial class CS2_SimpleAdmin
{
sb.Append(_localizer!["sa_adminchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
player.PrintToChat(sb.ToString());
foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
foreach (CCSPlayerController? p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
{
p.PrintToChat(sb.ToString());
}
@ -409,26 +477,37 @@ public partial class CS2_SimpleAdmin
public HookResult OnCommandTeamSay(CCSPlayerController? player, CommandInfo info)
{
if (player == null || !player.IsValid || player.IsBot)
{
return HookResult.Continue;
}
if (info.GetArg(1).StartsWith($"/")
|| info.GetArg(1).StartsWith($"!"))
{
return HookResult.Continue;
}
if (info.GetArg(1).Length == 0)
{
return HookResult.Handled;
}
if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag, out _) || PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence, out _))
{
return HookResult.Stop;
}
if (!info.GetArg(1).StartsWith($"@")) return HookResult.Continue;
if (!info.GetArg(1).StartsWith($"@"))
{
return HookResult.Continue;
}
StringBuilder sb = new();
if (AdminManager.PlayerHasPermissions(new SteamID(player.SteamID), "@css/chat"))
{
sb.Append(_localizer!["sa_adminchat_template_admin", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
foreach (var p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
foreach (CCSPlayerController? p in Utilities.GetPlayers().Where(p => p.IsValid && p is { IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
{
p.PrintToChat(sb.ToString());
}
@ -437,7 +516,7 @@ public partial class CS2_SimpleAdmin
{
sb.Append(_localizer!["sa_adminchat_template_player", player.PlayerName, info.GetArg(1).Remove(0, 1)]);
player.PrintToChat(sb.ToString());
foreach (var p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
foreach (CCSPlayerController? p in Utilities.GetPlayers().Where(p => p is { IsValid: true, IsBot: false, IsHLTV: false } && AdminManager.PlayerHasPermissions(new SteamID(p.SteamID), "@css/chat")))
{
p.PrintToChat(sb.ToString());
}
@ -449,12 +528,16 @@ public partial class CS2_SimpleAdmin
private void OnMapStart(string mapName)
{
if (Config.OtherSettings.ReloadAdminsEveryMapChange && ServerLoaded && ServerId != null)
{
ReloadAdmins(null);
}
AddTimer(1.0f, ServerManager.CheckHibernationStatus);
if (!ServerLoaded || ServerId == null)
{
AddTimer(1.5f, OnGameServerSteamAPIActivated);
}
// AddTimer(34, () =>
// {
@ -470,15 +553,17 @@ public partial class CS2_SimpleAdmin
[GameEventHandler]
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
{
var player = @event.Userid;
CCSPlayerController? player = @event.Userid;
if (player?.UserId == null || !player.IsValid || player.IsHLTV ||
player.Connected != PlayerConnectedState.Connected || !PlayersInfo.ContainsKey(player.SteamID) ||
@event.Attacker == null)
{
return HookResult.Continue;
}
var playerPosition = player.PlayerPawn.Value?.AbsOrigin;
var playerRotation = player.PlayerPawn.Value?.AbsRotation;
CounterStrikeSharp.API.Modules.Utils.Vector? playerPosition = player.PlayerPawn.Value?.AbsOrigin;
QAngle? playerRotation = player.PlayerPawn.Value?.AbsRotation;
PlayersInfo[player.SteamID].DiePosition = new DiePosition(
new Vector3(
playerPosition?.X ?? 0,
@ -498,12 +583,17 @@ public partial class CS2_SimpleAdmin
[GameEventHandler(HookMode.Pre)]
public HookResult OnPlayerTeam(EventPlayerTeam @event, GameEventInfo info)
{
var player = @event.Userid;
CCSPlayerController? player = @event.Userid;
if (player == null || !player.IsValid || player.IsBot || !SilentPlayers.Contains(player.Slot))
{
return HookResult.Continue;
}
if (@event is not { Oldteam: <= 1, Team: >= 1 })
{
return HookResult.Continue;
}
if (@event is not { Oldteam: <= 1, Team: >= 1 }) return HookResult.Continue;
SilentPlayers.Remove(player.Slot);
SimpleAdminApi?.OnAdminToggleSilentEvent(player.Slot, false);
@ -531,13 +621,22 @@ public partial class CS2_SimpleAdmin
/// </summary>
private void EnforceRename(CCSPlayerController? player)
{
if (player is null || !player.IsValid || player.IsBot) return;
if (!RenamedPlayers.TryGetValue(player.SteamID, out var name)) return;
if (player is null || !player.IsValid || player.IsBot)
{
return;
}
if (!RenamedPlayers.TryGetValue(player.SteamID, out string? name))
{
return;
}
AddTimer(0.5f, () =>
{
if (player.IsValid && !player.PlayerName.Equals(name))
{
player.Rename(name);
}
});
}
}
}