cs2-SimpleAdmin/CS2-SimpleAdmin/CS2-SimpleAdmin.cs
Salvatore 460d8b2b85 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
2026-10-21 18:51:18 +02:00

300 lines
9.6 KiB
C#

using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Commands.Targeting;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using CS2_SimpleAdmin.Database;
using CS2_SimpleAdmin.Managers;
using CS2_SimpleAdmin.Menus;
using CS2_SimpleAdminApi;
using Microsoft.Extensions.Logging;
using MySqlConnector;
namespace CS2_SimpleAdmin;
[MinimumApiVersion(369)]
public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig<CS2_SimpleAdminConfig>
{
internal static CS2_SimpleAdmin Instance { get; private set; } = new();
public override string ModuleName
{
get
{
if (Helper.IsDebugBuild)
{
return "CS2-SimpleAdmin (DEBUG)";
}
return "CS2-SimpleAdmin (RELEASE)";
}
}
public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)";
public override string ModuleAuthor => "daffyy";
public override string ModuleVersion => "1.9.1";
public override void Load(bool hotReload)
{
Instance = this;
if (hotReload)
{
ServerLoaded = false;
_serverLoading = false;
CacheManager?.Dispose();
CacheManager = new CacheManager();
// OnGameServerSteamAPIActivated();
OnMapStart(string.Empty);
AddTimer(6.0f, () =>
{
if (DatabaseProvider == null)
{
return;
}
PlayersInfo.Clear();
CachedPlayers.Clear();
BotPlayers.Clear();
foreach (CCSPlayerController? player in Utilities.GetPlayers().Where(p => p.IsValid && p is { Connected: PlayerConnectedState.Connected, IsHLTV: false }).ToArray())
{
if (!player.IsBot)
{
PlayerManager.LoadPlayerData(player, true);
}
else
{
BotPlayers.Add(player);
}
}
;
});
}
_cBasePlayerControllerSetPawnFunc = new MemoryFunctionVoid<CBasePlayerController, CCSPlayerPawn, bool, bool>(GameData.GetSignature("CBasePlayerController_SetPawn"));
SimpleAdminApi = new Api.CS2_SimpleAdminApi();
Capabilities.RegisterPluginCapability(ICS2_SimpleAdminApi.PluginCapability, () => SimpleAdminApi);
PlayersTimer?.Kill();
PlayersTimer = null;
PlayerManager.CheckPlayersTimer();
Menus.MenuManager.Instance.InitializeDefaultCategories();
BasicMenu.Initialize();
}
public override void OnAllPluginsLoaded(bool hotReload)
{
UiKit.Preload(Logger);
RegisterEvents();
AddTimer(0.5f, RegisterCommands.InitializeCommands);
// Admins are reloaded by ServerManager once ServerId is known (or on its failure paths).
// Loading the plugin mid-map (css_plugins load) fires no map start, so kick server loading off ourselves.
AddTimer(5.0f, () =>
{
if (!ServerLoaded)
{
OnGameServerSteamAPIActivated();
}
});
if (!CoreConfig.UnlockConCommands)
{
_logger?.LogError(
$"⚠️ Warning: 'UnlockConCommands' is disabled in core.json. " +
$"Players will not be automatically banned when kicked and will be able " +
$"to rejoin the server for 60 seconds. " +
$"To enable instant banning, set 'UnlockConCommands': true"
);
}
}
public void OnConfigParsed(CS2_SimpleAdminConfig config)
{
Helper.UpdateConfig(config);
_logger = Logger;
Config = config;
UiKit.Configure(config.MenuConfigs);
bool missing = false;
string cssPath = Path.Combine(Server.GameDirectory, "csgo", "addons", "counterstrikesharp");
string pluginsPath = Path.Combine(cssPath, "plugins");
string sharedPath = Path.Combine(cssPath, "shared");
foreach (string plugin in _requiredPlugins)
{
string pluginDirPath = Path.Combine(pluginsPath, plugin);
string pluginDllPath = Path.Combine(pluginDirPath, $"{plugin}.dll");
if (!Directory.Exists(pluginDirPath))
{
_logger?.LogError(
$"❌ Plugin directory '{plugin}' missing at: {pluginDirPath}"
);
missing = true;
}
if (!File.Exists(pluginDllPath))
{
_logger?.LogError(
$"❌ Plugin DLL '{plugin}.dll' missing at: {pluginDllPath}"
);
missing = true;
}
}
foreach (string shared in _requiredShared)
{
string sharedDirPath = Path.Combine(sharedPath, shared);
string sharedDllPath = Path.Combine(sharedDirPath, $"{shared}.dll");
if (!Directory.Exists(sharedDirPath))
{
_logger?.LogError(
$"❌ Shared library directory '{shared}' missing at: {sharedDirPath}"
);
missing = true;
}
if (!File.Exists(sharedDllPath))
{
_logger?.LogError(
$"❌ Shared library DLL '{shared}.dll' missing at: {sharedDllPath}"
);
missing = true;
}
}
if (missing)
{
Server.ExecuteCommand($"css_plugins unload {ModuleDirectory}");
return;
}
Instance = this;
if (Config.DatabaseConfig.DatabaseType.Contains("mysql", StringComparison.CurrentCultureIgnoreCase))
{
if (string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseHost) ||
string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseName) ||
string.IsNullOrWhiteSpace(config.DatabaseConfig.DatabaseUser))
{
throw new Exception("[CS2-SimpleAdmin] You need to setup MySQL credentials in config!");
}
if (!Enum.TryParse(config.DatabaseConfig.DatabaseSSlMode, true, out MySqlSslMode sslMode))
{
sslMode = MySqlSslMode.Preferred;
}
MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder()
{
Server = config.DatabaseConfig.DatabaseHost,
Database = config.DatabaseConfig.DatabaseName,
UserID = config.DatabaseConfig.DatabaseUser,
Password = config.DatabaseConfig.DatabasePassword,
Port = (uint)config.DatabaseConfig.DatabasePort,
SslMode = sslMode,
Pooling = true,
};
DbConnectionString = builder.ConnectionString;
DatabaseProvider = new MySqlDatabaseProvider(DbConnectionString);
}
else
{
if (string.IsNullOrWhiteSpace(config.DatabaseConfig.SqliteFilePath))
{
throw new Exception("[CS2-SimpleAdmin] You need to specify SQLite file path in config!");
}
DatabaseProvider = new SqliteDatabaseProvider(ModuleDirectory + "/" + config.DatabaseConfig.SqliteFilePath);
}
(bool success, string? exception) = Task.Run(() => DatabaseProvider.CheckConnectionAsync()).GetAwaiter().GetResult();
if (!success)
{
if (exception != null)
{
Logger.LogError("Problem with database connection! \n{exception}", exception);
}
Unload(false);
return;
}
Task.Run(() => DatabaseProvider.DatabaseMigrationAsync());
if (!Directory.Exists(ModuleDirectory + "/data"))
{
Directory.CreateDirectory(ModuleDirectory + "/data");
}
_localizer = Localizer;
if (!string.IsNullOrEmpty(Config.Discord.DiscordLogWebhook))
{
DiscordWebhookClientLog = new DiscordManager(Config.Discord.DiscordLogWebhook);
}
if (Config.EnableUpdateCheck)
{
Task.Run(async () => await PluginInfo.CheckVersion(ModuleVersion, Logger));
}
PermissionManager = new PermissionManager(DatabaseProvider);
BanManager = new BanManager(DatabaseProvider);
MuteManager = new MuteManager(DatabaseProvider);
WarnManager = new WarnManager(DatabaseProvider);
}
internal static TargetResult? GetTarget(CommandInfo command, int argument = 1)
{
TargetResult matches = command.GetArgTargetResult(argument);
if (!matches.Any())
{
command.ReplyToCommand($"Target {command.GetArg(argument)} not found.");
return null;
}
if (command.GetArg(argument).StartsWith('@'))
{
return matches;
}
if (matches.Count() == 1)
{
return matches;
}
command.ReplyToCommand($"Multiple targets found for \"{command.GetArg(argument)}\".");
return null;
}
public override void Unload(bool hotReload)
{
CacheManager?.Dispose();
CacheManager = null;
PlayersTimer?.Kill();
PlayersTimer = null;
UnregisterEvents();
if (hotReload)
{
PlayersInfo.Clear();
}
else
{
Server.ExecuteCommand($"css_plugins unload {ModuleDirectory}");
}
}
}